js基础-深拷贝/扁平化/数组去重/节流/防抖/promise/大数相加
深拷贝/扁平化/数组去重/节流/防抖
背景
这一些操作都是老生常谈了,是必拿分,不能出任何差错。
手写一遍,方便记忆。循序渐进,不要想着一步到位。
深拷贝
- 乞丐版本。JSON.stringify() / JSON.parse()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let obj = { a: 1, b: { w: 4 } } let obj1 = JSON.parse(JSON.stringify(obj)) console.log(obj1)
let obj2 = { a: 1, b: { w: 4, k: undefined, func() {}, arr: [undefined, 1, function () {}] }, } let obj3 = JSON.parse(JSON.stringify(obj2)) console.log(obj3)
|
- 基础版
1 2 3 4 5 6 7 8 9 10 11 12 13
| function deepClone(target) { if (typeof target !== 'object') { return target } let cloneTarget = {} for (let key in target) { cloneTarget[key] = deepClone(target[key]) } return cloneTarget }
|
- 考虑数组。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function deepClone(target) { if (typeof target !== 'object') { return target } let cloneTarget = Array.isArray(target) ? [] : {} for (let key in target) { cloneTarget[key] = deepClone(target[key]) } return cloneTarget }
|
- 循环引用。如果有循环引用,如:
1 2
| const obj = {} obj.a = obj
|
会造成堆栈溢出:
1
| Uncaught RangeError: Maximum call stack size exceeded
|
解决方法是用空间来存储已经遍历过的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function deepClone(target, map = new Map()) { if (typeof target !== 'object') { return target } let cloneTarget = Array.isArray(target) ? [] : {} if (map.get(target)) { return map.get(target) } else { map.set(target, cloneTarget) } for (let key in target) { cloneTarget[key] = deepClone(target[key], map) } return cloneTarget }
|
扁平化
- 简单类型, toString() / join()
1 2 3 4
| let arr = [1, 5, [8, 8, 6, [6, 4, 3]]] let arr1 = arr.toString().split(',').map(Number) let arr2 = arr.join(',').split(',').map(Number)
|
- reduce
1 2 3 4 5 6 7 8
| const flatten = (arr) => { return arr.reduce((cur, next) => { return cur.concat(Array.isArray(next) ? flatten(next) : next) }, []) }
console.log(flatten([1, 5, [8, 8, 6, [6, 4, 3]]]))
|
- 递归
1 2 3 4 5 6 7 8 9 10 11 12 13
| var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]] function flatten(arr) { var res = [] for (let i = 0, length = arr.length; i < length; i++) { if (Array.isArray(arr[i])) { res = res.concat(flatten(arr[i])) } else { res.push(arr[i]) } } return res } flatten(arr1)
|
- flat 最简单的方法
1 2
| let arr = [1, 5, [8, 8, 6, [6, 4, 3, { a: 1, b: 2 }]]].flat(Infinity)
|
数组去重
- Array.filter() + indexOf O(n2)
1 2 3 4 5
| function unique(arr) { return arr.filter((item, index) => { return arr.indexOf(item) === index }) }
|
- 双重 for 循环 O(n2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function unique(arr) { let len = arr.length for (let i = 0; i < len; i++) { for (let j = i + 1; j < len; j++) { if (arr[i] == arr[j]) { arr.splice(j, 1) len-- j-- } } } return arr }
|
- for…of + includes() O(n2)
1 2 3 4 5 6 7
| function unique(arr) { let result = [] for (let i of arr) { !result.includes(i) && result.push(i) } return result }
|
- Array.sort() + for O(nlogn)
1 2 3 4 5 6 7 8 9
| function unique(arr) { arr = arr.sort() let result = [arr[0]]
for (let i = 1, len = arr.length; i < len; i++) { arr[i] !== arr[i - 1] && result.push(arr[i]) } return result }
|
- new Set()
1 2 3 4
| function unique(arr) { return Array.from(new Set(arr)) return [...new Set(arr)] }
|
- for…of + Object O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13
| function unique(arr) { let result = [] let obj = {}
for (let i of arr) { if (!obj[i]) { result.push(i) obj[i] = 1 } }
return result }
|
节流
每隔一段时间,只执行一次函数。
场景:
- 滚动加载,加载更多或滚到底部监听
- 高频点击提交,表单重复提交
- mousemove
- 执行一次,离开后不执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function throttle(func, wait) { var context, args var previous = 0
return function () { var now = +new Date() context = this args = arguments if (now - previous > wait) { func.apply(context, args) previous = now } } }
|
- 第一次不执行,离开后再执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function throttle(func, wait) { var context, args var timeout
return function () { context = this args = arguments if (!timeout) { timeout = setTimeout(() => { timeout = null func.apply(context, args) }, wait) } } }
|
- 组合版
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
| function throttle(func, wait) { var timeout, context, args, result var previous = 0
var later = function () { previous = +new Date() timeout = null func.apply(context, args) }
var throttled = function () { var now = +new Date() var remaining = wait - (now - previous) context = this args = arguments if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout) timeout = null } previous = now func.apply(context, args) } else if (!timeout) { timeout = setTimeout(later, remaining) } } return throttled }
|
防抖
最后一次操作完几秒后再执行。只需触发一次回调。
场景:
搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测
窗口大小 Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
- 初版
1 2 3 4 5 6 7 8
| function debounce(func, wait) { var timeout return function () { clearTimeout(timeout) timeout = setTimeout(func, wait) } }
|
- this 和 参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function debounce(func, wait) { var timeout
return function () { var context = this var args = arguments
clearTimeout(timeout) timeout = setTimeout(function () { func.apply(context, args) }, wait) } }
|
- 立即执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function debounce(func, wait, immediate) { var timeout
return function () { var context = this var args = arguments
if (timeout) clearTimeout(timeout) if (immediate) { var callNow = !timeout timeout = setTimeout(function () { timeout = null }, wait) if (callNow) func.apply(context, args) } else { timeout = setTimeout(function () { func.apply(context, args) }, wait) } } }
|
promise
https://github.com/xieranmaya/blog/issues/3
大数相加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var addStrings = function (num1, num2) { let i = num1.length - 1, j = num2.length - 1, add = 0 const ans = [] while (i >= 0 || j >= 0 || add != 0) { const x = i >= 0 ? +num1.charAt(i) : 0 const y = j >= 0 ? +num2.charAt(j) : 0 const result = x + y + add ans.push(result % 10) add = Math.floor(result / 10) i -= 1 j -= 1 } return ans.reverse().join('') }
|
-
版权声明: 本博客所有文章除特别声明外,均采用
CC BY 4.0 CN协议
许可协议。转载请注明出处!
Жизнь, как качели - то вверх, то вниз.