https://juejin.im/post/6854573212890562573
前言
JS是前端的核心,但有些使用技巧你还不一定知道;本文梳理了JS的41个技巧,帮助大家提高JS的使用技巧
目录
Array
1.数组交集
2.数组并集
3.数组差集
4.数组补集
5.数组去重
6.数组排序
7.最大值
8.数组求和
9.数组合并
10.数组是否包含值
11.数组每一项都满足
12.数组有一项满足
13.版本号排序
14. 对象转数组
15.数组转对象
16.数组解构
Object
17.对象变量属性
18.对象多余属性删除
19.对象嵌套属性解构
20.解构对象属性别名
21.解构对象属性默认值
22.拦截对象
23.对象深度拷贝
24.对象是否相等
25.对象转化为字符串
Function
26.函数隐式返回值
27.函数自执行
28.函数异步执行
String
29.字符串翻转
30.url参数序列化
31.url参数反序列化
32.转化为字符串
Number
33.数字千分位
34.字符串转数字
35.判断小数是否相等
36.双位运算符
37.取整和奇偶性判断
Boolean
38.判断数据类型
39.使用Boolean过滤数组假值
40.短路运算
41.switch 简写
普通数组
const arr1 = [1, 2, 3, 4, 5 , 8 ,9],arr2 = [5, 6, 7, 8, 9];const interp = arr1.filter(function (val) { return arr2.indexOf(val) > -1 })
console.log(interp) //[5, 8, 9]
数组对象
数组对象目前仅针对value值为简单的Number,String,Boolan数据类型 文中JSON.stringif比较对象是简写方法,完整的对象比较请看技巧24.对象是否相等
const arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name5', id: 5 }];
const arr2 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
const result = arr2.filter(function (v) {return arr1.some(n => JSON.stringify(n) === JSON.stringify(v))
})
console.log(result); // [{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name5', id: 5 }]
普通数组
const arr1 = [1, 2, 3, 4, 5, 8, 9]
const arr2 = [5, 6, 7, 8, 9];
const result = arr1.concat(arr2.filter(v => !arr1.includes(v)))
console.log(result) //[1, 2, 3, 4, 5, 8, 9, 6, 7]
数组对象
const arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
const arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
let arr3 = arr1.concat(arr2);
let result = [];
let obj = [];
result = arr3.reduce(function (prev, cur, index, arr) {obj[cur.id] ? '' : obj[cur.id] = true && prev.push(cur);return prev;
}, []);
console.log(result); //[{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]
数组arr1相对于arr2所没有的
普通数组
const arr1 = [1, 2, 3, 4, 5, 8, 9]
const arr2 = [5, 6, 7, 8, 9];
const diff = arr1.filter(item => !new Set(arr2).has(item))
console.log(diff) //[ 1, 2, 3, 4 ]
数组对象
// 对象数组
let arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
let arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
let result = arr1.filter(function (v) {return arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
})
console.log(result); // [ { name: 'name2', id: 2 }, { name: 'name3', id: 3 } ]
两个数组各自没有的集合
普通数组
const arr1 = [1, 2, 3, 4, 5, 8, 9]
const arr2 = [5, 6, 7, 8, 9];
const difference = Array.from(new Set(arr1.concat(arr2).filter(v => !new Set(arr1).has(v) || !new Set(arr2).has(v))))
console.log(difference) //[ 1, 2, 3, 4, 6, 7 ]
数组对象
let arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
let arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
let arr3 = arr1.concat(arr2);
let result = arr3.filter(function (v) {return arr1.every(n => JSON.stringify(n) !== JSON.stringify(v)) || arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
})
console.log(result); // [{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]
总结一下,差集就是数组arr1相对于arr2所没有的集合,补集是两个数组各自没有的集合
普通数组
console.log(Array.from(new Set([1, 2, 3, 3, 4, 4]))) //[1,2,3,4]
console.log([...new Set([1, 2, 3, 3, 4, 4])]) //[1,2,3,4]
数组对象
const arr = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];const result = [];arr.forEach(item=>{!result.some(v => JSON.stringify(v) === JSON.stringify(item)) && result.push(item)})console.log(result) //[{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]
普通数组
console.log([1, 2, 3, 4].sort((a, b) => a - b)); // [1, 2,3,4] 升序
console.log([1, 2, 3, 4].sort((a, b) => b - a)); // [4,3,2,1] 降序
数组对象
const arr1 = [{ name: "Rom", age: 12 }, { name: "Bob", age: 22 }].sort((a, b) => { return a.age - b.age })//升序
const arr2 = [{ name: "Rom", age: 12 }, { name: "Bob", age: 22 }].sort((a, b) => { return -a.age + b.age })//降序
console.log(arr2) // [{ name: 'Bob', age:22 }, { name: 'Rom', age: 12 }]
console.log(arr1) // [ { name: 'Rom', age: 12 }, { name: 'Bob', age: 22 } ]
两个种类型数组都可以使用sort排序,sort是浏览器内置方法;
默认是升序排序,默认返回一个函数,有两个参数:
(a, b) => a - b 是升序;
(a, b) => b - a 是降序。
普通数组
Math.max(...[1, 2, 3, 4]) //4
Math.max.apply(this, [1, 2, 3, 4]) //4
[1, 2, 3, 4].reduce((prev, cur, curIndex, arr) => {return Math.max(prev, cur);
}, 0) //4
取数组对象中id的最大值
const arr = [{ id: 1, name: 'jack' },{ id: 2, name: 'may' },{ id: 3, name: 'shawn' },{ id: 4, name: 'tony' }]
const arr1 = Math.max.apply(Math, arr.map(item => { return item.id }))
const arr2 = arr.sort((a, b) => { return b.id - a.id })[0].id
console.log(arr1) // 4
console.log(arr2) // 4
普通数组
[1, 2, 3, 4].reduce(function (prev, cur) {return prev + cur;
}, 0) //10
数组对象
const sum = [{age:1},{age:2}].reduce(function (prev, cur) {return prev + cur.age;
}, 0) //3
console.log(sum)
普通数组
const arr1 =[1, 2, 3, 4].concat([5, 6]) //[1,2,3,4,5,6]
const arr2 =[...[1, 2, 3, 4],...[4, 5]] //[1,2,3,4,5,6]
const arrA = [1, 2], arrB = [3, 4]
const arr3 =[].concat.apply(arrA, arrB)//arrA值为[1,2,3,4]
数组对象
const arr4 = [{ age: 1 }].concat([{ age: 2 }])
const arr5 = [...[{ age: 1 }],...[{ age: 2 }]]
console.log(arr4) //[ { age: 1 }, { age: 2 } ]
console.log(arr5) // [ { age: 1 }, { age: 2 } ]
普通数组
console.log([1, 2, 3].includes(4)) //false
console.log([1, 2, 3].indexOf(4)) //-1 如果存在换回索引
console.log([1, 2, 3].find((item) => item === 3)) //3 如果数组中无值返回undefined
console.log([1, 2, 3].findIndex((item) => item === 3)) //2 如果数组中无值返回-1
数组对象
const flag = [{age:1},{age:2}].some(v=>JSON.stringify(v)===JSON.stringify({age:2}))
console.log(flag)
普通数组
[1, 2, 3].every(item => { return item > 2 })
数组对象
const arr = [{ age: 3 }, { age: 4 }, { age: 5 }]
arr.every(item => { return item.age > 2 }) // true
普通数组
[1, 2, 3].some(item => { return item > 2 })
数组对象
const arr = [{ age: 3 }, { age: 4 }, { age: 5 }]
arr.some(item &#61;> { return item.age < 4 }) // true
方法一
function sortNumber(a, b) {return a - b
}
const b &#61; [1,2,3,7,5,6]
const a &#61; ["1.5", "1.5", "1.40", "1.25", "1.1000", "1.1"];console.log(a.sort(sortNumber)); // [ 1, 2, 3, 5, 6, 7 ]
console.log(b.sort(sortNumber)); //[ &#39;1.1000&#39;, &#39;1.1&#39;, &#39;1.25&#39;, &#39;1.40&#39;, &#39;1.5&#39;, &#39;1.5&#39; ]
可见sort排序对整数可以&#xff0c;类似版本号这个格式就不适用了&#xff0c;因为sort函数在比较字符串的时候&#xff0c;是比较字符串的Unicode进行排序的。
方法二
//假定字符串的每节数都在5位以下
//去除数组空值||空格
if (!Array.prototype.trim) {Array.prototype.trim &#61; function () {let arr &#61; []; this.forEach(function (e) {if (e.match(/\S&#43;/)) arr.push(e);})return arr;}
}//提取数字部分
function toNum(a) {let d &#61; a.toString();let c &#61; d.split(/\D/).trim();let num_place &#61; ["", "0", "00", "000", "0000"], r &#61; num_place.reverse();for (let i &#61; 0; i < c.length; i&#43;&#43;) {let len &#61; c[i].length;c[i] &#61; r[len] &#43; c[i];}let res &#61; c.join(&#39;&#39;);return res;
}//提取字符
function toChar(a) {let d &#61; a.toString();let c &#61; d.split(/\.|\d/).join(&#39;&#39;);return c;
}function sortVersions(a, b) {let _a1 &#61; toNum(a), _b1 &#61; toNum(b);if (_a1 !&#61;&#61; _b1) return _a1 - _b1;else {_a2 &#61; toChar(a).charCodeAt(0).toString(16);_b2 &#61; toChar(b).charCodeAt(0).toString(16);return _a2 - _b2;}
}let arr1 &#61; ["10", "5", "40", "25", "1000", "1"];
let arr2 &#61; ["1.10", "1.5", "1.40", "1.25", "1.1000", "1.1"];
let arr3 &#61; ["1.10c", "1.10b", "1.10C", "1.25", "1.1000", "1.10A"];
console.log(arr1.sort(sortVersions)) //[ &#39;1&#39;, &#39;5&#39;, &#39;10&#39;, &#39;25&#39;, &#39;40&#39;, &#39;1000&#39; ]
console.log(arr2.sort(sortVersions)) //[ &#39;1.1&#39;, &#39;1.5&#39;, &#39;1.10&#39;, &#39;1.25&#39;, &#39;1.40&#39;, &#39;1.1000&#39; ]
console.log(arr3.sort(sortVersions)) // [ &#39;1.10A&#39;, &#39;1.10C&#39;, &#39;1.10b&#39;, &#39;1.10c&#39;, &#39;1.25&#39;, &#39;1.1000&#39; ]
可以看出这个函数均兼容整数&#xff0c;非整数&#xff0c;字母&#xff1b;
字母排序是根据Unicode排序的&#xff0c;所以1.10b在1.10C的后面
将数组的key和value转化成数组
Object.keys({ name: &#39;张三&#39;, age: 14 }) //[&#39;name&#39;,&#39;age&#39;]
Object.values({ name: &#39;张三&#39;, age: 14 }) //[&#39;张三&#39;,14]
Object.entries({ name: &#39;张三&#39;, age: 14 }) //[[name,&#39;张三&#39;],[age,14]]
Object.fromEntries([name, &#39;张三&#39;], [age, 14]) //ES10的api,Chrome不支持 , firebox输出{name:&#39;张三&#39;,age:14}
将数组的值转化为对象的value
const arrName &#61; [&#39;张三&#39;, &#39;李四&#39;, &#39;王五&#39;]
const arrAge&#61;[&#39;20&#39;,&#39;30&#39;,&#39;40&#39;]
const arrDec &#61; [&#39;描述1&#39;, &#39;描述2&#39;, &#39;描述3&#39;]
const obj &#61; arrName.map((item,index)&#61;>{return { name: item, age: arrAge[index],dec:arrDec[index]}
})console.log(obj) // [{ name: &#39;张三&#39;, age: &#39;20&#39;, dec: &#39;描述1&#39; },{ name: &#39;李四&#39;, age: &#39;30&#39;, dec: &#39;描述2&#39; },{ name: &#39;王五&#39;, age: &#39;40&#39;, dec: &#39;描述3&#39; }]
const arr&#61;[1,2]; //后面一定要加分号&#xff0c;因为不加解释器会认为在读数组
[arr[1], arr[0]] &#61; [arr[0], arr[1]]; // [2,1]
const flag &#61; true;
const obj &#61; {a: 0,[flag ? "c" : "d"]: 2
};
// obj &#61;> { a: 0, c: 2 }
const { name, age, ...obj } &#61; { name: &#39;张三&#39;, age: 13, dec: &#39;描述1&#39;, info: &#39;信息&#39; }
console.log(name) // 张三
console.log(age) // 13
console.log(obj) // {dec: &#39;描述1&#39;, info: &#39;信息&#39; }
const { info:{ dec} } &#61; { name: &#39;张三&#39;, age: 13, info:{dec: &#39;描述1&#39;, info: &#39;信息&#39; }}
console.log(dec) // 描述1
const { name:newName } &#61; { name: &#39;张三&#39;, age: 13 }
console.log(newName) // 张三
const { dec&#61;&#39;这是默认dec值&#39; } &#61; { name: &#39;张三&#39;, age: 13 }
console.log(dec) //这是默认dec值
利用Object.defineProperty拦截对象
无法拦截数组的值
let obj &#61; { name: &#39;&#39;, age: &#39;&#39;, sex: &#39;&#39; },defaultName &#61; ["这是姓名默认值1", "这是年龄默认值1", "这是性别默认值1"];
Object.keys(obj).forEach(key &#61;> {Object.defineProperty(obj, key, { // 拦截整个object 对象&#xff0c;并通过get获取值&#xff0c;set设置值&#xff0c;vue 2.x的核心就是这个来监听get() {return defaultName;},set(value) {defaultName &#61; value;}});
});console.log(obj.name); // [ &#39;这是姓名默认值1&#39;, &#39;这是年龄默认值1&#39;, &#39;这是性别默认值1&#39; ]
console.log(obj.age); // [ &#39;这是姓名默认值1&#39;, &#39;这是年龄默认值1&#39;, &#39;这是性别默认值1&#39; ]
console.log(obj.sex); // [ &#39;这是姓名默认值1&#39;, &#39;这是年龄默认值1&#39;, &#39;这是性别默认值1&#39; ]
obj.name &#61; "这是改变值1";
console.log(obj.name); // 这是改变值1
console.log(obj.age); // 这是改变值1
console.log(obj.sex); // 这是改变值1let objOne &#61; {}, defaultNameOne &#61; "这是默认值2";
Object.defineProperty(obj, &#39;name&#39;, {get() {return defaultNameOne;},set(value) {defaultNameOne &#61; value;}
});
console.log(objOne.name); // undefined
objOne.name &#61; "这是改变值2";
console.log(objOne.name); // 这是改变值2
利用proxy拦截对象
let obj &#61; { name: &#39;&#39;, age: &#39;&#39;, sex: &#39;&#39; }
let handler &#61; {get(target, key, receiver) {console.log("get", key); return Reflect.get(target, key, receiver);},set(target, key, value, receiver) {console.log("set", key, value); // set name 李四 // set age 24return Reflect.set(target, key, value, receiver);}
};
let proxy &#61; new Proxy(obj, handler);
proxy.name &#61; "李四";
proxy.age &#61; 24;
defineProterty和proxy的对比&#xff1a;
1.defineProterty是es5的标准,proxy是es6的标准;
2.proxy可以监听到数组索引赋值,改变数组长度的变化;
3.proxy是监听对象,不用深层遍历,defineProterty是监听属性;
4.利用defineProterty实现双向数据绑定(vue2.x采用的核心)
JSON.stringify深度克隆对象;
1.无法对函数 、RegExp等特殊对象的克隆;
2.会抛弃对象的constructor,所有的构造函数会指向Object;
3.对象有循环引用,会报错
const mapTag &#61; &#39;[object Map]&#39;;
const setTag &#61; &#39;[object Set]&#39;;
const arrayTag &#61; &#39;[object Array]&#39;;
const objectTag &#61; &#39;[object Object]&#39;;
const argsTag &#61; &#39;[object Arguments]&#39;;const boolTag &#61; &#39;[object Boolean]&#39;;
const dateTag &#61; &#39;[object Date]&#39;;
const numberTag &#61; &#39;[object Number]&#39;;
const stringTag &#61; &#39;[object String]&#39;;
const symbolTag &#61; &#39;[object Symbol]&#39;;
const errorTag &#61; &#39;[object Error]&#39;;
const regexpTag &#61; &#39;[object RegExp]&#39;;
const funcTag &#61; &#39;[object Function]&#39;;const deepTag &#61; [mapTag, setTag, arrayTag, objectTag, argsTag];function forEach(array, iteratee) {let index &#61; -1;const length &#61; array.length;while (&#43;&#43;index < length) {iteratee(array[index], index);}return array;
}function isObject(target) {const type &#61; typeof target;return target !&#61;&#61; null && (type &#61;&#61;&#61; &#39;object&#39; || type &#61;&#61;&#61; &#39;function&#39;);
}function getType(target) {return Object.prototype.toString.call(target);
}function getInit(target) {const Ctor &#61; target.constructor;return new Ctor();
}function cloneSymbol(targe) {return Object(Symbol.prototype.valueOf.call(targe));
}function cloneReg(targe) {const reFlags &#61; /\w*$/;const result &#61; new targe.constructor(targe.source, reFlags.exec(targe));result.lastIndex &#61; targe.lastIndex;return result;
}function cloneFunction(func) {const bodyReg &#61; /(?<&#61;{)(.|\n)&#43;(?&#61;})/m;const paramReg &#61; /(?<&#61;\().&#43;(?&#61;\)\s&#43;{)/;const funcString &#61; func.toString();if (func.prototype) {const param &#61; paramReg.exec(funcString);const body &#61; bodyReg.exec(funcString);if (body) {if (param) {const paramArr &#61; param[0].split(&#39;,&#39;);return new Function(...paramArr, body[0]);} else {return new Function(body[0]);}} else {return null;}} else {return eval(funcString);}
}function cloneOtherType(targe, type) {const Ctor &#61; targe.constructor;switch (type) {case boolTag:case numberTag:case stringTag:case errorTag:case dateTag:return new Ctor(targe);case regexpTag:return cloneReg(targe);case symbolTag:return cloneSymbol(targe);case funcTag:return cloneFunction(targe);default:return null;}
}function clone(target, map &#61; new WeakMap()) {// 克隆原始类型if (!isObject(target)) {return target;}// 初始化const type &#61; getType(target);let cloneTarget;if (deepTag.includes(type)) {cloneTarget &#61; getInit(target, type);} else {return cloneOtherType(target, type);}// 防止循环引用if (map.get(target)) {return map.get(target);}map.set(target, cloneTarget);// 克隆setif (type &#61;&#61;&#61; setTag) {target.forEach(value &#61;> {cloneTarget.add(clone(value, map));});return cloneTarget;}// 克隆mapif (type &#61;&#61;&#61; mapTag) {target.forEach((value, key) &#61;> {cloneTarget.set(key, clone(value, map));});return cloneTarget;}// 克隆对象和数组const keys &#61; type &#61;&#61;&#61; arrayTag ? undefined : Object.keys(target);forEach(keys || target, (value, key) &#61;> {if (keys) {key &#61; value;}cloneTarget[key] &#61; clone(target[key], map);});return cloneTarget;
}console.log(clone({name: &#39;张三&#39;, age: 23,obj: { name: &#39;李四&#39;, age: 46 },arr: [1, 2, 3]
})) // { name: &#39;张三&#39;, age: 23, obj: { name: &#39;李四&#39;, age: 46 }, arr: [ 1, 2, 3 ] }
对象深度克隆实际上就是要兼容Array&#xff0c;RegExp&#xff0c;Date&#xff0c;Function类型&#xff1b;
克隆函数可以用正则取出函数体和参数&#xff0c;再定义一个函数将取出来的值赋值进去
详细请戳对象深度拷贝
如果用JSON.stringify转化属性顺序不同&#xff0c;也不相等&#xff1b;
而且不支持无法对函数 、RegExp等特殊对象的克隆
function deepCompare(x, y) {var i, l, leftChain, rightChain;function compare2Objects(x, y) {var p;// remember that NaN &#61;&#61;&#61; NaN returns false// and isNaN(undefined) returns trueif (isNaN(x) && isNaN(y) && typeof x &#61;&#61;&#61; &#39;number&#39; && typeof y &#61;&#61;&#61; &#39;number&#39;) {return true;}// Compare primitives and functions. // Check if both arguments link to the same object.// Especially useful on the step where we compare prototypesif (x &#61;&#61;&#61; y) {return true;}// Works in case when functions are created in constructor.// Comparing dates is a common scenario. Another built-ins?// We can even handle functions passed across iframesif ((typeof x &#61;&#61;&#61; &#39;function&#39; && typeof y &#61;&#61;&#61; &#39;function&#39;) ||(x instanceof Date && y instanceof Date) ||(x instanceof RegExp && y instanceof RegExp) ||(x instanceof String && y instanceof String) ||(x instanceof Number && y instanceof Number)) {return x.toString() &#61;&#61;&#61; y.toString();}// At last checking prototypes as good as we canif (!(x instanceof Object && y instanceof Object)) {return false;}if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {return false;}if (x.constructor !&#61;&#61; y.constructor) {return false;}if (x.prototype !&#61;&#61; y.prototype) {return false;}// Check for infinitive linking loopsif (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {return false;}// Quick checking of one object being a subset of another.// todo: cache the structure of arguments[0] for performancefor (p in y) {if (y.hasOwnProperty(p) !&#61;&#61; x.hasOwnProperty(p)) {return false;} else if (typeof y[p] !&#61;&#61; typeof x[p]) {return false;}}for (p in x) {if (y.hasOwnProperty(p) !&#61;&#61; x.hasOwnProperty(p)) {return false;} else if (typeof y[p] !&#61;&#61; typeof x[p]) {return false;}switch (typeof (x[p])) {case &#39;object&#39;:case &#39;function&#39;:leftChain.push(x);rightChain.push(y);if (!compare2Objects(x[p], y[p])) {return false;}leftChain.pop();rightChain.pop();break;default:if (x[p] !&#61;&#61; y[p]) {return false;}break;}}return true;}if (arguments.length < 1) {return true; }for (i &#61; 1, l &#61; arguments.length; i < l; i&#43;&#43;) {leftChain &#61; []; //Todo: this can be cachedrightChain &#61; [];if (!compare2Objects(arguments[0], arguments[i])) {return false;}}return true;
}const obj1 &#61; { name: &#39;张三&#39;, age: 23, obj: { name: &#39;李四&#39;, age: 46 }, arr: [1, 2, 3],date:new Date(23),reg: new RegExp(&#39;abc&#39;),fun: ()&#61;>{}}
const obj2 &#61; { name: &#39;张三&#39;, age: 23, obj: { name: &#39;李四&#39;, age: 46 }, arr: [1, 2, 3],date: new Date(23),reg: new RegExp(&#39;abc&#39;),fun: ()&#61;>{}}console.log(deepCompare(obj1,obj2)) // true
判断对象是否相等&#xff0c;实际上就是要处理Array&#xff0c;Date&#xff0c;RegExp&#xff0c;Object&#xff0c;Function的特殊类型是否相等
通过字符串&#43;Object 的方式来转化对象为字符串(实际上是调用 .toString() 方法)
&#39;the Math object:&#39; &#43; Math.ceil(3.4) // "the Math object:4"
&#39;the JSON object:&#39; &#43; {name:&#39;曹操&#39;} // "the JSON object:[object Object]"
覆盖对象的toString和valueOf方法来自定义对象的类型转换
2 * { valueOf: ()&#61;>&#39;4&#39; } // 8
&#39;J&#39; &#43; { toString: ()&#61;>&#39;ava&#39; } // "Java"
当&#43;用在连接字符串时&#xff0c;当一个对象既有toString方法又有valueOf方法时候&#xff0c;JS通过盲目使用valueOf方法来解决这种含糊;
对象通过valueOf方法强制转换为数字&#xff0c;通过toString方法强制转换为字符串
&#39;&#39; &#43; {toString:()&#61;>&#39;S&#39;,valueOf:()&#61;>&#39;J&#39;} //J
(()&#61;>3)() //3
(()&#61;>(3
))()
函数省略大括号&#xff0c;或者将大括号改成小括号可以确保代码以单个语句的形式进行求值
const Func &#61; function() {}(); // 常用(function() {})(); // 常用
(function() {}()); // 常用
[function() {}()];new function() {};
new function() {}();
void function() {}();
typeof function() {}();
delete function() {}();&#43; function() {}();
- function() {}();
~ function() {}();
! function() {}();
Promise
Promise.reject(&#39;这是第二个 reject 值&#39;).then((data)&#61;>{console.log(data)
}).catch(data&#61;>{console.log(data) //这是第二个 reject 值
})
Generator
function* gen(x) {const y &#61; yield x &#43; 6;return y;
}// yield 如果用在另外一个表达式中,要放在()里面
// 像上面如果是在&#61;右边就不用加()
function* genOne(x) {const y &#61; &#96;这是第一个 yield 执行:${yield x &#43; 1}&#96;;return y;
}const g &#61; gen(1);
//执行 Generator 会返回一个Object,而不是像普通函数返回return 后面的值
g.next() // { value: 7, done: false }
//调用指针的 next 方法,会从函数的头部或上一次停下来的地方开始执行&#xff0c;直到遇到下一个 yield 表达式或return语句暂停,也就是执行yield 这一行
// 执行完成会返回一个 Object,
// value 就是执行 yield 后面的值,done 表示函数是否执行完毕
g.next() // { value: undefined, done: true }
// 因为最后一行 return y 被执行完成,所以done 为 true
Async/Await
function getSomething() {return "something";
}
async function testAsync() {return Promise.resolve("hello async");
}
async function test() {const v1 &#61; await getSomething();const v2 &#61; await testAsync();console.log(v1, v2); //something 和 hello async
}
test();
function reverseStr(str &#61; "") {return str.split("").reduceRight((t, v) &#61;> t &#43; v);
}const str &#61; "reduce123";
console.log(reverseStr(str)); // "321recuder"
将对象序列化成url参数传递
function stringifyUrl(search &#61; {}) {return Object.entries(search).reduce((t, v) &#61;> &#96;${t}${v[0]}&#61;${encodeURIComponent(v[1])}&&#96;,Object.keys(search).length ? "?" : "").replace(/&$/, "");
}console.log(stringifyUrl({ age: 27, name: "YZW" })); // "?age&#61;27&name&#61;YZW"
一般会通过location.search拿到路由传递的参数&#xff0c;并进行反序列化得到对象
function parseUrlSearch() {const search &#61; &#39;?age&#61;25&name&#61;TYJ&#39;return search.replace(/(^\?)|(&$)/g, "").split("&").reduce((t, v) &#61;> {const [key, val] &#61; v.split("&#61;");t[key] &#61; decodeURIComponent(val);return t;}, {});
}console.log(parseUrlSearch()); // { age: "25", name: "TYJ" }
const val &#61; 1 &#43; ""; // 通过&#43; &#39;&#39;空字符串转化
console.log(val); // "1"
console.log(typeof val); // "string"const val1 &#61; String(1);
console.log(val1); // "1"
console.log(typeof val1); // "string"
方法一&#xff1a;
function thousandNum(num &#61; 0) {const str &#61; (&#43;num).toString().split(".");const int &#61; nums &#61;> nums.split("").reverse().reduceRight((t, v, i) &#61;> t &#43; (i % 3 ? v : &#96;${v},&#96;), "").replace(/^,|,$/g, "");const dec &#61; nums &#61;> nums.split("").reduce((t, v, i) &#61;> t &#43; ((i &#43; 1) % 3 ? v : &#96;${v},&#96;), "").replace(/^,|,$/g, "");return str.length > 1 ? &#96;${int(str[0])}.${dec(str[1])}&#96; : int(str[0]);
}thousandNum(1234); // "1,234"
thousandNum(1234.00); // "1,234"
thousandNum(0.1234); // "0.123,4"
console.log(thousandNum(1234.5678)); // "1,234.567,8"
方法二
console.log(&#39;1234567890&#39;.replace(/\B(?&#61;(\d{3})&#43;(?!\d))/g, ","))
console.log((1234567890).toLocaleString())
方法一
用*1来转化为数字,实际上是调用.valueOf方法
&#39;32&#39; * 1 // 32
&#39;ds&#39; * 1 // NaN
null * 1 // 0
undefined * 1 // NaN
1 * { valueOf: ()&#61;>&#39;3&#39; } // 3
方法二
&#43; &#39;123&#39; // 123
&#43; &#39;ds&#39; // NaN
&#43; &#39;&#39; // 0
&#43; null // 0
&#43; undefined // NaN
&#43; { valueOf: ()&#61;>&#39;3&#39; } // 3
肯定有人会说这还不简单&#xff0c;直接用&#39;&#61;&#61;&#61;&#39;比较&#xff1b;
实际上0.1&#43;0.2 !&#61;&#61;0.3&#xff0c;因为计算机不能精确表示0.1&#xff0c; 0.2这样的浮点数&#xff0c;所以相加就不是0.3了
Number.EPSILON&#61;(function(){ //解决兼容性问题return Number.EPSILON?Number.EPSILON:Math.pow(2,-52);
})();
//上面是一个自调用函数&#xff0c;当JS文件刚加载到内存中&#xff0c;就会去判断并返回一个结果
function numbersequal(a,b){ return Math.abs(a-b)
const a&#61;0.1&#43;0.2, b&#61;0.3;
console.log(numbersequal(a,b)); //这里就为true了
双位运算符比Math.floor(),Math.ceil()速度快
~~7.5 // 7
Math.ceil(7.5) // 8
Math.floor(7.5) // 7~~-7.5 // -7
Math.floor(-7.5) // -8
Math.ceil(-7.5) // -7
所以负数时&#xff0c;双位运算符和Math.ceil结果一致&#xff0c;正数时和Math.floor结果一致
取整
3.3 | 0 // 3
-3.9 | 0 // -3parseInt(3.3) // 3
parseInt(-3.3) // -3// 四舍五入取整
Math.round(3.3) // 3
Math.round(-3.3) // -3// 向上取整
Math.ceil(3.3) // 4
Math.ceil(-3.3) // -3// 向下取整
Math.floor(3.3) // 3
Math.floor(-3.3) // -4
判断奇偶数
const num&#61;5;
!!(num & 1) // true
!!(num % 2) // true
function dataTypeJudge(val, type) {const dataType &#61; Object.prototype.toString.call(val).replace(/\[object (\w&#43;)\]/, "$1").toLowerCase();return type ? dataType &#61;&#61;&#61; type : dataType;
}
console.log(dataTypeJudge("young")); // "string"
console.log(dataTypeJudge(20190214)); // "number"
console.log(dataTypeJudge(true)); // "boolean"
console.log(dataTypeJudge([], "array")); // true
console.log(dataTypeJudge({}, "array")); // false
可判断类型&#xff1a;undefined、null、string、number、boolean、array、object、symbol、date、regexp、function、asyncfunction、arguments、set、map、weakset、weakmap
const compact &#61; arr &#61;> arr.filter(Boolean)
compact([0, 1, false, 2, &#39;&#39;, 3, &#39;a&#39;, &#39;e&#39; * 23, NaN, &#39;s&#39;, 34]) //[ 1, 2, 3, &#39;a&#39;, &#39;s&#39;, 34 ]
||&#xff08;或&#xff09;
const flag &#61; false || true //true
// 某个值为假时可以给默认值
const arr &#61; false || []
&&&#xff08;与&#xff09;
const flag1 &#61; false && true //false
const flag2 &#61; true && true //true
可以用对象替代switch&#xff0c;提高代码可读性
switch(a) {case &#39;张三&#39;:return &#39;age是12&#39;case &#39;李四&#39;:return &#39;age是120&#39;
}// 使用对象替换后
const obj &#61;{&#39;张三&#39;: &#39;age12&#39;,&#39;李四&#39;: &#39;age120&#39;,
}
console.log(obj[&#39;张三&#39;])
源码地址 https://github.com/lanzhsh/react-vue-koa/tree/master/js/skill&#xff1b;
原创码字不易&#xff0c;欢迎start&#xff01;
回复 【idea激活】即可获得idea的激活方式
回复 【Java】获取java相关的视频教程和资料
回复 【SpringCloud】获取SpringCloud相关多的学习资料
回复 【python】获取全套0基础Python知识手册
回复 【2020】获取2020java相关面试题教程
回复 【加群】即可加入终端研发部相关的技术交流群
最近于哥也在搞视频号啦&#xff0c;主要针对于程序员方向的&#xff0c;大家可以去关注一波&#xff1a;
最近面试Java后端开发的感受
互联网的圈子&#xff0c;游戏行业的现状是如何&#xff1f;
动画&#xff1a;一招学会TCP的三次握手和四次挥手
干掉PostMan&#xff01;IDEA这款插件太实用了…
美团面试题&#xff1a;Java-线程池 ThreadPool 专题详解
当去阿里面试 Java 都是问什么?
Linux 最常用命令&#xff08;简单易学&#xff0c;但能解决 95% 以上的问题&#xff09;
相信自己&#xff0c;没有做不到的&#xff0c;只有想不到的
在这里获得的不仅仅是技术&#xff01;
喜欢就给个“在看”