常用的 JavaScript 方法。
Dom 相关
事件代理
1 2 3 4 5 6 7 8
| document.getElementById("father-id").onClick = function(event) { event = event || window.event; const target = event.target || event.srcElement; if (target.nodeName.toLowerCase() === 'xxx') { } }
|
数组相关
数组扁平化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function flatten(arr) { if (!Array.isArray(arr)) return []; let _array = []; arr.forEach(function(elem) { if (Array.isArray(elem)) { _array = _array.concat(flatten(elem)); } else { _array.push(elem); } }) return _array; }
const array = [1, [2], [[3]], [[[4]]]]; const arrayFlatten= flatten(array); console.log('array before flatten', array); console.log('array after flatten', arrayFlatten);
|
运行结果:
1 2
| array before flatten (4) [1, Array(1), Array(1), Array(1)] array after flatten: [1, 2, 3, 4]
|
去重
方法 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function unique(array) { const set = new Set(); const arrayUnique = []; array.forEach(function (elem) { if (set.has(elem)) return;
// 元素不存在,添加到数组中 arrayUnique.push(elem); set.add(elem); }) return arrayUnique; }
console.log(unique([1,2,2,2,3,4,5]))
|
方法 2
1 2 3 4 5
| function unique(array) { return [...new Set(array)]; }
console.log(unique([1,2,2,2,3,4,5]));
|
运行结果:
拷贝
浅拷贝
方法 1
1 2 3 4 5 6 7 8
| function copy(obj) { const result = Array.isArray(obj) ? [] : {}; [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)].foreach(key => { result[key] = obj[key] }) return result; }
|
方法 2
1 2 3
| function copy(obj) { return Array.isArray(obj) ? [...obj] : { ...obj }; }
|
方法 3
1 2 3
| function copy(obj) { return Object.assign(Array.isArray(obj) ? [] : {}, obj); }
|
深拷贝
方法 1,使用 JSON.stringify(JSON.parse(obj))
缺点:
- 遇到
undefined
, NaN
, -Infinity
, Infinity
, function
,RegExp
会默认转为 null
,
- 遇到函数会报错
Date
对象会转为字符串
- 循环引用时候回报错(a = {}, a.b = a 这种情况)
1 2 3
| function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); }
|
上面的方法遇到函数的话,JSON 将会序列化错误,可以使用 JSON.parse 以及 JSON.stringify 的第二个参数,对函数以及其他数据类型进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function handleParse(key, value) { console.log(value); return eval(value); }
function handleStringify(key, value) { console.log(value, typeof value); if (typeof value === "function") { return `i = ${value}`; }
if (value instanceof RegExp) { return `${value}`; }
if ( typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value) ) { return `new Date('${value}')`; } return value; }
function deepCopy(obj) { return JSON.parse(JSON.stringify(obj, handleStringify), handleParse); }
|
方法 2
解决了循环引用以及键名为 Symbol 的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| function deepCopy(obj, cached = new Map()) { if (typeof obj === "function") {
return eval(`i = ${obj}`); }
if (typeof obj !== "object") return obj;
if (obj instanceof RegExp) {
return eval(`${obj}`); }
if (obj instanceof Date) {
return eval(`new Date(${obj.getTime()})`); }
if (cached.has(obj)) { return cached.get(obj); }
const result = Array.isArray(obj) ? [] : {};
cached.set(obj, result);
[...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)].forEach(function ( key ) { result[key] = deepCopy(obj[key], cached); });
return result; }
|
字符串
去空格
方法 1,正则表达式替换
1 2 3 4 5
| function myTrim(str) { return str.replace(/(^\s*)|(\s*$)/g, ""); }
console.log(myTrim(" ok ").length);
|
方法 2, 算出第一个不是空格的位置,以及最后一个不是空格的位置,然后截取字符对应的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function myTrim(str) { let start = 0; let end = str.length - 1; for (; start < str.length; start++) { if (str[start] !== " ") break; }
for (; end >= 0; end--) { if (str[end] !== " ") break; }
return str.slice(start, end + 1); }
console.log(myTrim(" ok ").length);
|
方法 3: 字符串对象内置方法 trim()
1 2
| let str = " ok "; console.log(str.trim());
|
字符串全排列
广度优先实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function combine(str) { if (str.length === 1) { return [str]; }
const result = []; for (let i = 0; i < str.length; i++) { const curChar = str[i]; const combineArr = combine(str.slice(0, i) + str.slice(i + 1)); for (let j = 0; j < combineArr.length; j++) { result.push(curChar + combineArr[j]); } } return [...new Set(result)]; }
console.log("combine(abc)", combine("abc")); console.log("combine(aab)", combine("aab"));
|
深度优先实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function combine(str) { const result = []; (function _combine(str, path = "") { if (str.length === 0) { return result.push(path + str); }
for (let i = 0; i < str.length; i++) { _combine(str.slice(0, i) + str.slice(i + 1), path + str[i]); } })(str); return [...new Set(result)]; }
console.log("combine(abc)", combine("abc")); console.log("combine(aab)", combine("aab"));
|
排序和查找
插入排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function sortInsert(arr) { for (let i = 1; i < arr.length; i++) { for (let j = i; j >= 0; j--) { if (arr[j] < arr[j - 1]) { [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]; } else { break; } } }
return arr; }
console.log(sortInsert([2, 5, 1, 2, 5, 61, 21]));
|
归并排序
并归排序就是将数组分隔成小组,对小组进行排序,等小组有序后,再将有序的小组合并成有序的大组,对数组递归使用并归排序,最终就会得到一个有序的数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function sortMerge(arr) { if (arr.length === 1) { return arr; }
const indexMid = ~~(arr.length / 2);
const [part1, part2] = [ sortMerge(arr.slice(0, indexMid)), sortMerge(arr.slice(indexMid)), ];
console.log("part", part1, part2);
let result = [];
while (part1.length > 0 && part2.length > 0) { result.push(part1[0] < part2[0] ? part1.shift() : part2.shift()); }
result = [...result, ...part1, ...part2];
return result; }
console.log(sortMerge([2, 5, 1, 2, 5, 61, 21]));
|
快速排序
选取一个数值作为参考值,数组其他值与其比较,比它小的在其左侧,比它大的在其右侧,这样这个参考值在数组中的位置就确定了,然后继续递归对左右的数组进行快速排序,最后就会得到一个有序的数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function sortQuickly(arr) { if (arr.length === 0) return [];
const indexMid = Math.floor(arr.length / 2); let arrLeft = []; let arrRight = [];
for (let i = 0; i < arr.length; i++) { if (i === indexMid) continue; if (arr[i] < arr[indexMid]) { arrLeft.push(arr[i]); } else { arrRight.push(arr[i]); } }
return [...sortQuickly(arrLeft), arr[indexMid], ...sortQuickly(arrRight)]; }
console.log(sortQuickly([2, 5, 1, 2, 5, 61, 21]));
|
二位查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function binarySearch(arr, target) { if (arr.length === 0 || (arr.length === 1 && arr[0] !== target)) { return -1; }
const indexMid = arr.length >> 1; const valueMid = arr[indexMid];
if (valueMid === target) { return indexMid; } else if (target < valueMid) { return binarySearch(arr.slice(0, indexMid - 1), target); } else { return indexMid + 1 + binarySearch(arr.slice(indexMid + 1), target); } }
console.log(binarySearch([1, 2, 3, 6, 9, 11, 28, 42, 81, 98], 81));
|
另一种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function binarySearch(arr, target) { let indexLeft = 0, indexRight = arr.length;
while (indexLeft <= indexLeft) { const indexMid = (indexLeft + indexRight) >> 1; const valueMid = arr[indexMid]; if (target === valueMid) { return indexMid; } else if (target < valueMid) { indexRight = indexMid - 1; } else { indexLeft = indexMid + 1; } } return -1; }
console.log(binarySearch([1, 2, 3, 6, 9, 11, 28, 42, 81, 98], 81));
|
查找出现得最多的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function getMost(arr) { let map = new Map(); arr.forEach((item) => { if (map.has(item)) { map.set(item, map.get(item) + 1); } else { map.set(item, 1); } });
let [maxCount, maxValues] = [map.get(arr[0]), [arr[0]]]; map.forEach((count, value) => { if (count > maxCount) { maxCount = count; maxValues = [value]; } else if (count === maxCount) { maxValues.push(value); } });
return maxValues; } console.log(getMost(["1", "2", "3", "3", "55", "3", "55", "55", "12", "12"]));
|
功能函数
使用 setTimeout 实现 setInterval
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function myInterval(fn, interval, ...args) { let isContinuous = true; (function _interval() { setTimeout(() => { fn.apply(this, args); if (isContinuous) { _interval(fn, interval, ...args); } }, interval); })();
return () => { isContinuous = false; }; }
const stop = myInterval(() => { console.log("going on"); }, 1000);
|
节流和防抖
codepen 实例,请点击 => https://codepen.io/WeicoMY/pen/OJRvPPj
防抖
防抖就是防止抖动,事件持续时间内触发的话只会触发最后一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function debounce(fn, interval, context) { let timeId = undefined;
return (...args) => { if (timeId) { clearTimeout(timeId); timeId = undefined; }
timeId = setTimeout(() => { fn.apply(context, args); }, interval); }; }
|
节流
节流,节省流量,限制触发的频率,限制一段时间内触发的次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function throttle(fn, interval, context) { let isBusy = false;
return (...args) => { if (isBusy) return; isBusy = true; fn.apply(context, args); setTimeout(() => { isBusy = false; }, interval); }; }
|
原生 api 的实现
bind
1 2 3 4 5
| Function.prototype.myBind = (context, ...argsBind) => { return (...argsPass) => { this.apply(context, [...argsBind, ...argsPass]); }; };
|
call
1 2 3 4 5 6 7
| Function.prototype.myCall = function (context, ...args) { const __randomKey = Math.random().toString().slice(2); context[__randomKey] = this; const result = context[__randomKey](...args); delete context[__randomKey]; return result; };
|
apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Function.prototype.myApply = function (context, args) { const __randomKey = Math.random().toString().slice(2); context[__randomKey] = this; const result = context[__randomKey](...args); delete context[__randomKey]; return result; };
var a = 1; var obj = { a: 2, }; let func = function (args) { console.log("args: ", args); console.log("this.a is: ", this.a); };
func("global.a"); func.myApply(obj, ["obj.a"]);
|
instanceof
1 2 3 4 5 6 7 8 9 10 11
| function myInstanceOf(child, parent) { while (child.__proto__) { if (child.__proto__.constructor === parent) { return true; } child = child.__proto__; } return false; }
myInstanceOf([], Array);
|
new
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function myNew(consFunc, ...args) {
const _newObj = {};
_newObj.__proto__ = consFunc.prototype;
const result = consFunc.apply(_newObj, args);
return typeof result === "object" ? result : _newObj; }
|
reduce()
1 2 3 4 5 6 7 8 9 10 11 12 13
| Array.prototype.myReduce = (func, init) => {
const hasInit = init !== undefined init = init || this[0]; for(let i = hasInit ? 0 : 1; i < this.length i++) { init = func(init, this[i], i, this); } return init; }
|
forEach()
1 2 3 4 5
| Array.prototype.myForEach = (func) => { for (let i = 0; i < this.length; i++) { func(this[i], i, this); } };
|
isArray()
1 2 3 4
| Array.myIsArray = (elem) => { return Object.prototype.toString.apply(elem).slice(8, -1) === "Array"; };
|
sleep()
1 2 3 4 5 6 7
| function sleep(time) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, time); }); }
|
Promise()