文章目录
- 前言
- 对象的构造函数和类
- 向Number内置类的原型上扩展方法
- 判断是否给实例设置属性name
- Promise笔试题-02
- prototype和__proto__的笔试题
- JavaScript引用类型值值操和运算符优先级比较--笔试
- 原型与原型链--笔试-05
- 闭包--笔试-11
- this--基础知识(指向)-07
- 浅浅克隆和浅克隆(clone)--01
- 深克隆(clone)--01
- 作用域-笔试
- this总结-06
- 数据类型转换
- ==(两等) 和 !(非) 的优先级比较
- 访问器属性-13
- 变量提升案例
- 设计模式-观察者模式
- 箭头函数概念
- var、let和const的区别
- 一维数组升为二维数组
- promise的使用
- 字符串去重(以逗号隔开的字符串)
- 字符串去重
- vue+transition-group实现拖动排序
- 数组对象排序
- 正则表达式实现简单模板数据填充
- defineReactive函数,利用闭包封装Object.defineProperty()
- JavaScript获取字符串(string)第一个字符
- JavaScript递归实现数组扁平化(数组降维)
- JavaScript中number和~~比较、API
- JavaScript实现单词、字母排序
- JavaScript实现分组排序单词、单词分组排序、不能实现中文首拼音排序
- 待定
前言
每一个大标题,曾经都是一篇文章,此文章内容比较散乱。
对象的构造函数和类
创建对象
// 使用字面量创建对象
let starL = {Uname: '刘邦',Uage: 30
};
console.log(starL);// 使用构造函数创建对象
function Star(Uname, Uage) {this.Uname = Uname;this.Uage = Uage;
};
let starW = new Star('王之涣', 23); // 实例化对象
console.log(starW);
创建类
class Poet {// 类的共有属性放到 constructor 里面,// constructor 是构造器或者构造函数constructor(Uname, age) {this.Uname = Uname;this.age = age;};// 注意: 方法与方法之间不需要添加逗号humVerse(song) {console.log(this.Uname + '吟' + song);};
};
var PoetW = new Poet('王安石', 26);
console.log(PoetW);
PoetW.humVerse('元日');
向Number内置类的原型上扩展方法
编写plus和minus实现如下需求
// anonymous: 匿名函数的名称
// 闭包的作用是防止函数跟全局函数有冲突
// 函数自调方式
// ~ function() {}();
// (function() {})()
~ function anonymous(proto) {// 封装 plus 函数 和 minus 函数 共同部分// 使用函数表达式方式创建函数是为了保证函数提升和// 防止在创建函数前调用函数const checkNum = function checkNum(num) {// 强制转换为 Number 类型num = Number(num);// 如果 num 不是有效数字if (isNaN(num)) {// 给 num 赋值为 0num = 0;}// 返回 numreturn num;};// 函数表达式// 把匿名函数改为具名函数,在全局通过函数名无法访问此函数proto.plus = function plus(num) {// 在函数内部可以调用此函数// 调用方式 Number.prototype.plus()// 既不跟外部函数名字冲突// 又可以通过特殊方式调用// => this: 要操作的那个数字实例(对象) // => 返回 Number 类的实例,实现链式写法return this + checkNum(num);};proto.minus = function minus(num) {return this - checkNum(num);};
}(Number.prototype);let n = 10;
let m = n.plus(10).minus(5);
console.log(m); // => 15 (10+10-5)
// 向 Number 内置类(原型)上扩展方法
// 创建一个数据类型值:
// 1.字面量方式
// 2.构造函数方式
// 不论哪一种方式,创建出来的结果都是所属类的实例
// => 基本数据类型两种创建方式是不一样的: 字面量创建的是基本类型值,
// 构造函数方式创建的是引用类型值
// let x = 10; // 字面量方式
// let y = new Number(10); // 构造函数方式
// console.log(y.valueof() === x);
// => 对象结果的原始值是基本类型数字 10
判断是否给实例设置属性name
function C1(name) {// => name: undefined 没有给实例设置私有的属性nameif (name) this.name = name;
}function C2(name) {// => 给实例设置私有属性 name this.name = undefindthis.name = name;
}function C3(name) {// => 给实例设置私有属性 name this.name = undefind// 因为 name 的值为 undefined// 所以走 || 后面的代码this.name = name || 'join';
}C1.prototype.name = 'Tom';
C2.prototype.name = 'Tom';
C3.prototype.name = 'Tom';console.log((new C1().name) + (new C2().name) + (new C3().name));// new C1().name
// 没有传参 函数里面的 name 为 undefined
// 实例里面没有 name 找原型上的 'Tom'// new C2().name
// 没有传参 函数里面的的 name 为 undefined
// 但是函数确实添加了 name 属性 只是值为 undefined 而已
// 所以找私有属性 name 的值为 undefined// new C3().name
// 没有传参 函数里面的 name 为 undefined
// 但是函数确实添加了 name 属性 只是值为 'join'
// 所以私有属性 name 的值为 'join'// 最终结果为 'Tomundefinedjoin'
Promise笔试题-02
console.log(1);
// 宏仁务 2
setTimeout(_ => {console.log(2);
}, 1000);async function fn() {console.log(3);// 宏仁务 3setTimeout(_ => {console.log(4);}, 20);// 结果为失败// 所以 微任务 1 // 报错永远不会执行return Promise.reject();
}async function run() {console.log(5);await fn();// 微任务 1console.log(6);
}
run();// 需要执行 150 MS 左右
for (let i = 0; i < 90000000; i++) {}// 宏仁务 4
setTimeout(_ => {console.log(7);// 立即执行new Promise(resolve => {console.log(8);// 微任务 5resolve();}).then(_ => {console.log(9);})
}, 0);console.log(10);// 1
// 5
// 3
// 10
// Uncaught (in promise) undefined
// 7
// 8
// 9
// 4
// 2
prototype和__proto__的笔试题
function Fn() {// 代码字符串// 谁 new 就是谁的私有属性this.x = 100;this.y = 200;this.getX = function() {console.log(this.x);};// 每个函数都有 prototype 对象// 所以 prototype 是一个堆 Fn.prototype// Fn.prototype 对象也带 __proto__ 属性// 这个对象上有 constructor// constructor 指向函数本身
};// 把 getX 挂载到 Fn 的原型对象上
// 生成一个新的堆内存
// 属于公共属性
Fn.prototype.getX = function() {console.log(this.x);
};// 把 getY 挂载到 Fn 的原型对象上
// 生成一个新的堆内存
// 属于公共属性
Fn.prototype.getY = function() {console.log(this.y);
};// 括号可加可不不加
// 都是创建类的实例
// 创建实例对象 f1 / f2 (堆)
// 同时创建 x: 100 y: 200 getX: function() { ... }
// 都是这个实例的私有属性
// 通过 this 创建的变量和对象都属于私有属性
// 实例都带 __proto__ 属性
// __proto__ 所属类的原型 prototype
// 通过原型链往上找都属于 实例(Fn.prototype) 的共有属性
let f1 = new Fn();
let f2 = new Fn;// Object内置类(堆)
// 有自己的 prototype 原型对象
// prototype 指向 Object.prototype 原型对象
// 有自己的 __proto__ 属性
// 有自己的 constructor 属性
// constructor 指向 Object
// Object.prototype 的 __proto__ 指向的是 null// 实例指向的堆内存不同,如果自己私有就不会往原型类上查找
// 因为这个 getX 函数自己私有
// 所以两个实例的 getX 方法不相等
console.log(f1.getX === f2.getX); // false// 因为 getY 这个函数不是自己私有
// 所以往原型链上查找,结果相等
// 原因就是他们的 __proto__ 都指向 Fn.prototype 原型对象
console.log(f1.getY === f2.getY); // true// 通过原型链查找到的是公共的 getY
// 类似于 f1.getY === f2.getY
console.log(f1.__proto__.getY === Fn.prototype.getY); // true// f1.__proto__.getX 找的是 Fn.prototype 公共属性
// f2.getX 因为自己私有
// 所以不相等
console.log(f1.__proto__.getX === f2.getX); // false// f1.getX 属于自己私有的属性
// Fn.prototype.getX 本身就是公共属性
// 所以不相等
console.log(f1.getX === Fn.prototype.getX); // false// f1 自己身上没有 constructor
// 往原型对象上查找
// 又因为 constructor 指向 函数本身
// 所以结果就是 Fn 函数
console.log(f1.constructor); // Fn 函数// Fn.prototype 的 __proto__ 指向的是 Object.prototy
// 因为 Object.prototy 的 constructor 指向 Object 本身
// 所以是结果 Object
console.log(Fn.prototype.__proto__.constructor); // ƒ Object() { [native code] }// f1.getX() 执行
// 并且 f1.getX 是属于自己私有
// 所以输出 100
// 函数执行前面有点
f1.getX(); // 100// f1.__proto__.getX();
// 直接查找的是原型对象上的 getX()
// 原型上的是 console.log(this.x);
// 因为 f1.__proto__.getX(); getX() 前面有点
// 所以 相当于 f1.__proto__.x
// f1.__proto__.x 直接跳过自己(f1 实例对象(堆))往原型对象上查找
// 此时原型对象上也没有,再往 Object.prototype 对象上查找
// Object 对象上也没有
// 最后得到的是 undefined
f1.__proto__.getX(); // undefined// 与 f1.getX(); 相同
f2.getY(); // 200// Fn.prototype.getY();
// 直接查找的是原型对象上的 getY()
// 原型上的是 console.log(this.y);
// 因为 Fn.prototype.getY(); getY() 前面有点
// 所以 相当于 Fn.prototype.y
// Fn.prototype.y 直接跳过自己(f2 实例对象(堆))往原型对象上查找
// 此时原型对象上也没有,再往 Object.prototype 对象上查找
// Object 对象上也没有
// 最后得到的是 undefined
Fn.prototype.getY(); // undefined
JavaScript引用类型值值操和运算符优先级比较–笔试
let a = { n : 1 };
let b = a;
a.x = a = { n: 2 };
// 如果改为这样
// a = a.x = { n: 2 };
// 结果还是一样
// JavaScript 中点(.)比 等号 (=) 的优先级高console.log(a.x); // undefined
// a.x = a = { n: 2 };
// 这段代码可以写成这样子
// a.x = { n: 2 };
// a = { n: 2 };
// 因为 a 是引用类型数据。
// 所以当 a.x = { n: 2 };时引用值的地址改变;
// 此时全局的 let a = { n: 1, x: { n: 2 } };
// 接着又执行 a = { n: 2 };时 a 引用地址已经改变。
// 最后输出的结果就是 : undefinedconsole.log(b.x); // { n: 2 }
// 经过上面 a 的操作,
// 全局的 a 值已经变为 a = { n: 1, x: { n: 2 } };
// 而此时 b 引用的地址一直没有改变
// 所以输出结果为 { n: 2 }
原型与原型链–笔试-05
function A() {};A.prototype.n = 1;
var b = new A(); // b 实例对象已经建立原型连接
// 原型对象指向被改变,不会切断 b 实例对象的的指向
A.prototype = {n: 2,m: 3
};
var c = new A(); // c 实例对象将根据新的原型建立连接
console.log(b.n, b.m); // 1 undefined 这里拿到是改变 prototype 之前的堆数据
console.log(c.n, c.m); // 2 3 这里拿到是改变 prototype 之后的堆数据
// 此题生成了两个堆内存
// 并且两个堆内存都有自己的实例存储 => b c
闭包–笔试-11
function fun() { var n = 9; // js 中强行给一个未声明的变量赋值,// 程序不会报错// 并且会自动在全局创建此变量add = function() { n++;}; return function() { console.log(n); };
};// 把 fun() 执行的结果赋值给 fn 变量
var fn = fun();// 此处调用的是全局的 add 函数,
// 因为全局的 add 函数作用域链引用着 fun 函数作用域对象
// 所以修改的是 fun 里面变量的值
add();
fn(); // 10// 把 fun() 执行的结果赋值给 fn2 变量
// 注意:这里的 fn2 所引用的是 fun() 执行后的地址
// 所以 fn 和 fn2 变量使用的地址是不同,结果也不相同
var fn2 = fun();
fn2(); // 9
add();
add();
fn2(); // 11
fn(); // 10
add();
fn(); // 10
this–基础知识(指向)-07
var who = { userName: '杜牧', age: 36, fun: function() { // 此时无法确定 this 指向console.log(`我叫${ this.userName },今年${ this.age }岁。`);}
};// this 指向 who 对象
who.fun(); // 我叫杜牧,今年36岁。var me = { userName: '李清照', age: 22
};me.fun = who.fun;
// this 指向 me 对象
me.fun(); // 我叫李清照,今年22岁。var onWindow = who.fun;
// 前面没有 点(.)
// this 指向 window
onWindow(); // 我叫undefined,今年undefined岁。
浅浅克隆和浅克隆(clone)–01
let oldObject = {name: '西施',age: 26,height: '163.30',obj: {city: '成都',street: '玉林路'},array: ['赵雷', '嬴政', '天明']
}; // 浅克隆 -- 只能处理一层数据结构的克隆
// 深克隆 -- 可处理超过一层或多层数据结构的克隆
function clone(oldObject) {let newObject = {};for (let key in oldObject) {newObject[key] = oldObject[key];};return newObject;
};// 浅浅克隆
// 引用同一个地址
let newData = oldObject;
console.log(oldObject === newData); // true// 浅克隆成功
console.log(oldObject == clone(oldObject)); // falselet cloneData = clone(oldObject);// 第一层可以克隆成功
cloneData.age = 30;
console.log('cloneData:', cloneData.age); // 30
console.log('oldObject:', oldObject.age); // 26// 第二层 对象
cloneData.obj.city = '杭州';
console.log('cloneData:', cloneData.obj.city); // 杭州
console.log('oldObject:', oldObject.obj.city); // 杭州// 第二层 数组
cloneData.array[1] = '荆轲';
console.log('cloneData:', cloneData.array[1]); // 荆轲
console.log('oldObject:', oldObject.array[1]); // 荆轲
深克隆(clone)–01
let oldObject = {name: '西施',age: 26,height: '163.30',score: null,friends: ['李白', '杜牧', '李商隐'],oldObj: {city: '广州',area: '越秀区',street: '明信路'}
};function clone(oldObject) {// 第一步:创建一个空对象let newObject = {};// 第二步:判断值是否为空// 如果给的值里面有 null 就直接返回 null// 如果不进行处理会输出 {} 空对象if (oldObject === null) {return null;}// 第三步:判断是否是数组// 如果是数组需要处理,// 如果不处理数组会转为类数组对象// { 0: "李白", 1: "杜牧", 2: "李商隐" }if ({}.toString.call(oldObject) === '[object Array]') {var newArray = [];// 数组克隆// 使用 splice() 方法实现// splice() 里面什么也没写,// 表示从数组第一项截取到末尾newArray = oldObject.slice();return newArray;}// 第四步:使用 for in 循环实现克隆for (let key in oldObject) {// 如果原对象属性值是原始类型,// 可以直接赋值// 原始类型值是直接复制副本if (typeof oldObject[key] !== 'object') {newObject[key] = oldObject[key];} else {// 当前属性不是原始类型值,// 再次调用(类似与递归) clone 函数,// 继续复制当前属性的值newObject[key] = clone(oldObject[key]);}}return newObject;
};// 如果是 false 说明克隆成功
console.log(oldObject === clone(oldObject)); // falselet cloneData = clone(oldObject);// 修改克隆后的第一层 age 的值
cloneData.age = 30;// 修改克隆后第二层 数组的值
cloneData.friends[2] = '杜甫';// 修改克隆后的二层 city 的值
cloneData.oldObj.city = '武汉';// 深克隆,第一层两个值的地址已经完全不一样了
console.log('oldObject:', oldObject.age); // 26
console.log('newObject:', cloneData.age); // 30// 深克隆,第二层 数组 两个值的地址已经完全不一样了
console.log('oldObject:', oldObject.friends[2]); // 李商隐
console.log('newObject:', cloneData.friends[2]); // 杜甫// 深克隆,第二层 对象 两个值的地址已经完全不一样了
console.log('oldObject:', oldObject.oldObj.city); // 广州
console.log('newObject:', cloneData.oldObj.city); // 武汉
作用域-笔试
var a = 0, b = 0;function A(a) {// A(1)执行// 1.A函数被重置为// function A(b) {// console.log(a + b++);// };// 2.执行打印console.log(a++),// 因为此时的a是形参,形参的值是传入的1,// 所以打印1,而且形参a执行了++,此时值为2A = function (b) {// A(2)调用情况// 这个a采用的是闭包中的a,而不是全局中的a,// 所以是2,b是形参也是2,所以打印的是4// b使用的是传入的形参而不是全局中的bconsole.log(a + b++);};// 调用函数A(1)的时候执行这一行代码,// 并且把函数重置console.log(a++);
};
A(1);
// 1
A(2);
// 4
this总结-06
1、函数是否是用
new
来调用?如果是则this
绑定新创建的对。
2、函数是否通过call
或者apply
来硬绑定调用?如果是则this
绑定的是指定的对象。
3、函数是否是被某个上下文对象调用?如果是则this
绑定这个上下文对象。
4、如果以上都不是,则使用默认绑定,点前是谁就是谁,函数调用一般指向window
比较多。
数据类型转换
在JavaScript中类型转换只有三种情况。
1、转换为布尔值,调用Boolean()方法。
2、转换为数字,调用Number()、parseInt()和parseFloat()方法。
3、转换为字符串,调用.toString()或者String()方法。
==(两等) 和 !(非) 的优先级比较
console.log([] == ![]); // true
1、!(非) 的优先级高于==(两等) ,右边[]是true,取返等于false。
2、一个引用类型和一个值去比较,会把引用类型转化成值类型。所以,[] => 0 。
3、最后0 == false的结果是true 。
访问器属性-13
let attribute = {id: 11,sname: '名字'
};// 给 attribute 对象添加两个属性:_age 和 age
// age 作为保镖保护 _age
// 第一步
Object.defineProperties(attribute, {// 先添加一个半隐藏的 _age_age: {// 值value: 26,// 可以改writable: true,// 半隐藏enumerable: false,// 双保险configurable: false,},// 再为 _age 添加一个保镖 -- 访问器属性age: {get: function() {return this._age;},set: function(value) {if (value >= 18 && value <= 65) {this._age = value;} else {throw Error('年龄必须介于18~65之间。');}},enumerable: true,configurable: false}
});console.log(attribute);
// {id: 11, sname: "名字", _age: 26}console.log(attribute.age);
// 26console.log(attribute._age);
// 26attribute.age = 27;
console.log(attribute.age);
// 27attribute.age = 16;
console.log(attribute.age);
// Uncaught Error: 年龄必须介于18~65之间。
变量提升案例
function funHoisting() {console.log(1);
};funHoisting(); // 2function funHoisting() {console.log(2);
};funHoisting(); // 2var funHoisting = 100;
// 在函数提升的时候 var fun 对程序没有任何影响,
// 等于没写
funHoisting();
// Uncaught TypeError: funHoisting is not a function
设计模式-观察者模式
// 有一家猎人工会,
// 其中每个猎人都具有发布任务(publish),
// 订阅任务(subscribe)的功能
// 他们都有一个订阅列表来记录谁订阅了自己
// 定义一个猎人类
// 包括姓名,级别,订阅列表function Hunter(name, level) {this.name = name;this.level = level;this.list = [];
};// 在Hunter原型上添加publish方法
Hunter.prototype.publish = function(money) {console.log(this.level + '猎人' + this.name + '寻求帮助');this.list.forEach(function(item, index) {item(money);});
};// 在Hunter原型上添加subscribe方法
Hunter.prototype.subscribe = function(targrt, fn) {console.log(this.level + '猎人' + this.name + '订阅了' + targrt.name);targrt.list.push(fn);
};// 猎人工会走来了几个猎人
let hunterMing = new Hunter('小明', '黄金');
let hunterJin = new Hunter('小金', '白银');
let hunterZhang = new Hunter('小张', '黄金');
let hunterPeter = new Hunter(' Peter ', '青铜');// Peter等级较低,
// 可能需要帮助,
// 所以小明,小金,小张都订阅了Peter
hunterMing.subscribe(hunterPeter, function(money) {console.log('小明表示:' + (money > 200 ? '' : '暂时很忙,不能给予帮助'));
});hunterJin.subscribe(hunterPeter, function() {console.log('小金表示:给予帮助');
});hunterZhang.subscribe(hunterPeter, function() {console.log('小金表示:给予帮助');
});// Peter遇到困难,赏金198寻求帮助
hunterPeter.publish(198);
箭头函数概念
1、不需要使用 function 关键字来创建函数。
2、可以极大的简写函数。
3、当要求动态上下文的时候,不能使用箭头函数,也就是 this 的固定化。
var、let和const的区别
1、var声明的变量会挂载在window上,而let和const声明的变量不会。
2、var声明变量存在变量提升,let和const不存在变量提升。
3、let和const声明形成块作用域。
4、同一作用域下var可以声明同名变量,而let和const不可以。
一维数组升为二维数组
function arrTrans(num, arr) {// 参数 num : 决定二维数组的个数// 参数 arr : 源数组const iconsArr = [];arr.forEach((item, index) => {// floor() 向下取整const page = Math.floor(index / num);// 向数组 iconsArr 添加数组// 数组下标就是 pageif (!iconsArr[page]) iconsArr[page] = [];iconsArr[page].push(item);});return iconsArr;
}console.log(arrTrans(5, [1, 1, 2, 3, 4, 5, 6, 7, 8]));
// [[1, 1, 2, 3, 4], [5, 6, 7, 8]]
promise的使用
function func1() {return new Promise((resolve) => {setTimeout(() => {resolve("func1 1000");}, 1000);});
};function func2() {return new Promise((resolve) => {setTimeout(() => {resolve("func1 2000");}, 2000);});
};function func3() {return new Promise((resolve) => {setTimeout(() => {resolve("func1 3000");}, 3000);});
};func1().then((result) => {console.log(result);return func3();}).then((result) => {console.log(result);return func2();}).then((result) => {console.log(result);});
字符串去重(以逗号隔开的字符串)
function stringDuplicateRemoval(str) {let obj = {},arr = str.split(","),result = [],i = 0;for (let i = 0; i < arr.length; i++) obj[arr[i]] = 1;for (result[i++] in obj);return result;
}console.log(stringDuplicateRemoval('1,2,3,3'));
// [1,2,3]console.log(stringDuplicateRemoval('1,2,3,3').toString());
// '1,2,3'
字符串去重
function stringDuplicateRemoval(str) {let newStr = "";for (let i = 0; i < str.length; i++) {if (newStr.indexOf(str[i]) == -1) newStr += str[i];}return newStr;
}console.log(this.stringDuplicateRemoval("110120140"));
// 1024
vue+transition-group实现拖动排序
<transition-group id='app' name="drog" tag="ul"><div draggable="true" v-for="(item, index) in lists" @dragstart="dragStart($event, index)" @dragover="allowDrop" @drop="drop($event, index)" v-bind:key="item">{{item}}</div>
</transition-group>
new Vue({el: '#app',data: {lists: ['1: apple', '2: banana', '3: orange', '4: melon']},methods: {// 取消默认行为allowDrop(e){e.preventDefault();},// 开始拖动dragStart(e, index){let tar = e.target;e.dataTransfer.setData('Text', index);if (tar.tagName.toLowerCase() == 'li') {// console.log('drag start')// console.log('drag Index: ' + index)}},// 放置drop(e, index){this.allowDrop(e);// console.log('drop index: ' + index);//使用一个新数组重新排序后赋给原变量let arr = this.lists.concat([]),dragIndex = e.dataTransfer.getData('Text');temp = arr.splice(dragIndex, 1);arr.splice(index, 0, temp[0]);// console.log('sort');this.lists = arr;}}
});
数组对象排序
let arr = [{id: 1,weather:'晴'}, {id: 2,weather:'小雨'}, {id: 3,weather:'大雨'}, {id: 4,weather:'小雨'}, {id: 5,weather:'阴'}, {id: 6,weather:'雪'}
];function sortData(a, b) {return b.id - a.id;
}arr.sort(sortData);console.log(arr);
正则表达式实现简单模板数据填充
let templateStr = '<h1>我买了一棵{{thing}},花了{{money}}元,好{{mood}}</h1>';
let data = {thing: '白菜',money: 5,mood: '激动'
};// 最简单的模板引擎的实现机理,
// 利用的是正则表达式中的 replace() 方法。
// replace() 的第二个参数可以是一个函数,
// 这个函数提供捕获的东西的参数,就是$1
// 结合 data 对象,即可进行智能的替换
function render(templateStr, data) {return templateStr.replace(/\{\{(\w+)\}\}/g, function (findStr, $1) {return data[$1];});
}console.log(render(templateStr, data));
// <h1>我买了一棵白菜,花了5元,好激动</h1>
defineReactive函数,利用闭包封装Object.defineProperty()
function defineReactive(data, key, val) {Object.defineProperty(data, key, {// 可枚举enumerable: true,// 可以被配置,比如可以被 deleteconfigurable: true,// getter get() { return val;},// setterset(newValue) {if (val === newValue) return false;val = newValue;}});
};
let obj = {};
defineReactive(obj, 'a', 10); // 设置 a 属性
console.log(obj.a); // 10 访问 a 的值
obj.a = 100; // 改变 a 的值
console.log(obj.a); // 100 访问改变后 a 的值
JavaScript获取字符串(string)第一个字符
let stringVal = "web半晨";
// 方式一
console.log(stringVal.charAt(0));
// 方式二
console.log(stringVal.substring(0, 1));
// 方式三
console.log(stringVal.substr(0, 1));
JavaScript递归实现数组扁平化(数组降维)
let recursionData = [{id: 1,name: "一级",children: [{id: 2,name: "二级-1",children: [{id: 7,name: "三级-1",children: [{id: 10,name: "四级-1",},],},{id: 8,name: "三级-2",},],},{id: 3,name: "二级-2",children: [{id: 5,name: "三级-3",},{id: 6,name: "三级-4",},],},{id: 4,name: "二级-3",children: [{id: 9,name: "三级-5",children: [{id: 11,name: "四级-2",},],},],},],},
],arr = [];function funRecursion(list) {for (let i in list) {arr.push({id: list[i].id,name: list[i].name,});if (list[i].children) funRecursion(list[i].children);}return arr;
}console.log(funRecursion(recursionData));
// [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]console.log(arr.map((item) => item.id).sort((a, b) => a - b));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
JavaScript中number和~~比较、API
function replaceNumber() {console.log(Number(null)); // 0console.log(~~null); // 0console.log("--------------------------------------------------------------");console.log(Number(undefined)); // NaNconsole.log(~~undefined); // 0console.log("--------------------------------------------------------------");console.log(Number(123)); // 123console.log(~~123); // 123console.log("--------------------------------------------------------------");console.log(Number("123")); // 123console.log(~~"123"); // 123console.log("--------------------------------------------------------------");console.log(Number("abc")); // NaNconsole.log(~~"abc"); // 0console.log("--------------------------------------------------------------");console.log(Number(NaN)); // NaNconsole.log(~~NaN); // 0
}replaceNumber();
JavaScript实现单词、字母排序
function alphabeticSorting(params) {return params.sort((a, b) => {let x = a.word,y = b.word;return x > y ? 1 : x < y ? -1 : 0;});
}let words = [{id: 1,word: "absolute",},{id: 2,word: "every",},{id: 3,word: "display",},{id: 4,word: "border",},{id: 5,word: "class",},{id: 6,word: "background",},{id: 7,word: "delete",},
];console.log(alphabeticSorting(words));
JavaScript实现分组排序单词、单词分组排序、不能实现中文首拼音排序
function groupSortWords(params) {let grouping = {},afterGrouping = [];params.forEach(item => {// 单词首字母转大写let capital = item.word.replace(/\b[a-zA-Z]+\b/g, items => {return items.charAt(0).toUpperCase();});// grouping[capital] 使用了对象不能有同一属性的原理// 相当于去重if (grouping[capital]) {grouping[capital].arr.push(item);} else {grouping[capital] = {groupingName: `${capital}`,arr: [item]}}});// 把对象变为数组for (const key in grouping) {if (Object.hasOwnProperty.call(grouping, key)) {const itemKye = grouping[key];afterGrouping.push(itemKye);}}// 外层数组排序afterGrouping = afterGrouping.sort((a, b) => {let x = a.groupingName,y = b.groupingName;return x > y ? 1 : x < y ? -1 : 0;});// 内层数组排序afterGrouping = afterGrouping.map(item => {item.arr = item.arr.sort((a, b) => {let x = a.word,y = b.word;return x > y ? 1 : x < y ? -1 : 0;});return item;})// 最终返回return afterGrouping;
}// 数据源
let words = [{id: 1652156,word: "absolute",},{id: 2365256,word: "every",},{id: 3156258,word: "display",},{id: 4695845,word: "border",},{id: 5125369,word: "class",},{id: 6985485,word: "background",},{id: 7156895,word: "delete",},{id: 8789651,word: "color",},{id: 9369529,word: "application",},{id: 1031562,word: "length",},
];// 调用函数
console.log(groupSortWords(words));