1.手写call方法 改变this指向
执行函数
1 2 3 4 5 6 7 8 9 10 11 12 Function .prototype .myCall = function (thisObj, ...args ) { const funcKey = Symbol ("funcKey" ); thisObj[funcKey] = this ; const res = thisObj[funcKey](...args); delete thisObj[funcKey]; return res; };
2.手写apply方法 和 call 不同之处只有接收的第二个参数是数组
1 2 3 4 5 6 7 8 9 10 11 12 Function .prototype .myApply = function (thisObj, args ) { const funcKey = Symbol ("funcKey" ); thisObj[funcKey] = this ; const res = thisObj[funcKey](...args); delete thisObj[funcKey]; return res }
3.手写bind方法
绑定 this 指向
接收部分参数
返回一个可执行函数,可接受剩余参数
1 2 3 4 5 6 7 8 9 10 11 12 13 Function .prototype .myBind = function (thisObj, ...args ) { return (...reArgs ) => { let funcKey = Symbol ("funcKey" ); thisObj[funcKey] = this ; const result = thisObj[funcKey](...args, ...reArgs); delete thisObj[funcKey]; return result; }; };
4.实现compose函数
接收多个函数作为参数,返回新函数
新函数从右至左一次指向传入的函数,前一个函数多输出作为下一函数的输入
compose(f, g, h) 等价于 (x) => f(g(h(x)))。
1 2 3 4 5 6 7 8 9 10 const compose = (...fn ) => { if (fn.length === 0 ) return (v ) => v const cps = fn.reduce ((pre, cur ) => { return (...args ) => pre (cur (...args)) }) return cps }
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function fn1 (x ) { return x + 1 ; }function fn2 (x ) { return x + 2 ; }function fn3 (x ) { return x + 3 ; }function fn4 (x ) { return x + 4 ; }const a = compose (fn1, fn2, fn3, fn4);console .log (a (1 ));
5.使用setTimeout实现setInterval 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const mySetInterval = (fn, t ) => { let timer = null const interval = ( ) => { timer = setTimeout (() => { fn () interval () }, t) } interval () return { cancel : () => { clearTimeout (timer) } } }
为什么要用 settimeout 模拟实现 setInterval?setInterval 的缺陷是什么?
扩展:使用 setInterval 实现 setTimeout
1 2 3 4 5 6 7 8 const mySetTimeout = (fn, t ) => { const timer = setInterval (() => { clearTimeout (timer) fn () }, t) }
6.实现发布订阅模式 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 class EventEmitter { events = {} constructor ( ) { this .events = {} } on (type, callback ) { if (!this .events [type]) { this .events [type] = [callback] } else { this .events [type].push (callback) } } off (type, callback ) { if (!this .events [type]) return this .events [type] = this .events [type].filter (item => { item !== callback }) } once (type, callback ) { function fn (...args ) { callback (...args) this .off (type, fn) } this .on (type, fn) } emit (type, ...args ) { if (!this .events [type]) return this .events [type].forEach (item => { item.apply (this , args) }); } }
发布订阅模式:
解耦事件的发布者和订阅者,使得发布者和订阅者不需要直接知道对方的存在
而是通过一个中间层(事件中心)进行通信
发布者:负责发布事件
订阅者:订阅事件并在事件发布时执行回调
7.数组去重 1 2 3 4 function uniqueArr (arr ) { return [...new Set (arr)] }
8.数组扁平化 实现一个方法使多维数组变成一维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const flatten = (arr ) => { let result = [] for (let i = 0 ; i < arr.length ; i++) { if (Array .isArray (arr[i])) { result = result.concat (flatten (arr[i])) } else { result.push (arr[i]) } } return result }
9.实现继承的方法
原型链继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Parent (name ) { this .name = name this .say = function ( ) { console .log ('hello world' ) } }Parent .prototype .play = function ( ) { console .log ('你好 世界' ) }function Children (name ) { }Children .prototype = new Parent ()
缺点:所有字类实例共享父类的引用属性,修改其中一个,所有实例都会被修改
2.构造函数继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Parent (name ) { this .name = name this .say = function ( ) { console .log ('hello world' ) } }Parent .prototype .play = function ( ) { console .log ('你好 世界' ) }function Children (name ) { Parent .call (this , name) }
缺点:无法继承父类的 prototype 属性上的方法
3.组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function Parent (name ) { this .name = name this .say = function ( ) { console .log ('hello world' ) } }Parent .prototype .play = function ( ) { console .log ('你好 世界' ) }function Children (name ) { Parent .call (this , name) }Children .prototype = new Parent ()
缺点:父类构造函数会执行两次,性能消耗增加
4.寄生虫继承(完美)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Parent (name ) { this .name = name this .say = function ( ) { console .log ('hello world' ) } }Parent .prototype .play = function ( ) { console .log ('你好 世界' ) }function Children (name ) { Parent .call (this , name) }Children .prototype = Object .create (Parent .prototype )Children .prototype .constructor = Children
10.手写 new 操作符
1 2 3 4 5 6 7 8 9 function myNew (fn, ...args ) { let obj = Object .create (fn.prototype ) let result = fn.apply (obj, args) return result instanceof Object ? result : obj }
11.实现深拷贝 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 const deepCopy = (obj, hash = new WeakMap () ) => { if (typeof obj !== 'object' || obj === null ) { return obj } if (obj instanceof Date ) { return new Data (obj.getTime ()) } if (typeof obj === 'function' ) { return obj } if (hash.has (obj)) { return hash.get (obj) } let target = Array .isArray (obj) ? [] : {} hash.set (obj, target) for (let key in obj) { if (obj.hasOwnProperty (key)) { target[key] = deepCopy (obj[key], hash) } } return target }
12.实现防抖函数 停下来一段时间后才执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const debounce = (fn, delay ) => { let timer = null let isImmediate = false return function (...args ) { if (!isImmediate) { fn.apply (this , args) isImmediate = true } if (timer) { clearTimeout (timer) } timer = setTimeout (() => { isImmediate = false fn.apply (this , args) }, delay) } }
13.实现节流函数 每隔一段时间执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const throttle = (fn, delay ) => { let flag = false return function (...args ) { if (flag === true ) return flag = true setTimeout (() => { fn.apply (this , args) flag = false }, delay) } }
14.手写 Promise 基本实现 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 const PENDING = 'pending' ;const FULFILLED = 'fulfilled' ;const REJECTED = 'rejected' ;class MyPromise { constructor (executor ) { try { executor (this .resolve , this .reject ); } catch (error) { this .reject (error); } } status = PENDING ; value = undefined ; reason = undefined ; onFulfilledCallbacks = []; onRejectedCallbacks = []; resolve = (value ) => { if (this .status !== PENDING ) return ; this .status = FULFILLED ; this .value = value; while (this .onFulfilledCallbacks .length ) { this .onFulfilledCallbacks .shift ()(this .value ); } }; reject = (reason ) => { if (this .status !== PENDING ) return ; this .status = REJECTED ; this .reason = reason; while (this .onRejectedCallbacks .length ) { this .onRejectedCallbacks .shift ()(this .reason ); } }; then (onFulfilled, onRejected ) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; const promise2 = new MyPromise ((resolve, reject ) => { const handleCallback = (callback, result ) => { setTimeout (() => { try { const x = callback (result); resolvePromise (promise2, x, resolve, reject); } catch (error) { reject (error); } }, 0 ); }; if (this .status === FULFILLED ) { handleCallback (onFulfilled, this .value ); } if (this .status === REJECTED ) { handleCallback (onRejected, this .reason ); } if (this .status === PENDING ) { this .onFulfilledCallbacks .push (() => { handleCallback (onFulfilled, this .value ); }); this .onRejectedCallbacks .push (() => { handleCallback (onRejected, this .reason ); }); } }); return promise2; } }
15.手写 Promise 实例方法 then 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 const runAsyncTask = (callback ) => { if (typeof queueMicrotask === "function" ) { queueMicrotask (callback); } else if (typeof MutationObserver === "function" ) { const divNode = document .createElement ("div" ); const obs = new MutationObserver (callback); obs.observe (divNode, { childList : true }); divNode.innerText = "change" ; } else { setTimeout (callback, 0 ); } };const PENDING = "pending" ;const FULFILLED = "fulfilled" ;const REJECTED = "rejected" ;class MyPromise { state = PENDING ; result = undefined ; #handlers = []; constructor (func ) { const resolve = (result ) => { if (this .state === PENDING ) { this .state = FULFILLED ; this .result = result; this .#handlers.forEach (({ onFulfilled } ) => { onFulfilled (this .result ); }); } }; const reject = (result ) => { if (this .state === PENDING ) { this .state = REJECTED ; this .result = result; this .#handlers.forEach (({ onRejected } ) => { onRejected (this .result ); }); } }; try { func (resolve, reject); } catch (error) { reject (error); } } then (onFulfilled, onRejected ) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x ) => x; onRejected = typeof onRejected === "function" ? onRejected : x => { throw x }; const p2 = new MyPromise ((resolve, reject ) => { if (this .state === FULFILLED ) { runAsyncTask (() => { try { const x = onFulfilled (this .result ); resolvePromise (x, p2, resolve, reject); } catch (error) { reject (error); } }); } else if (this .state === REJECTED ) { runAsyncTask (() => { try { const x = onRejected (this .result ); resolvePromise (x, p2, resolve, reject); } catch (error) { reject (error); } }); } else if (this .state === PENDING ) { this .#handlers.push ({ onFulfilled : () => { runAsyncTask (() => onFulfilled (this .result )); }, onRejected : () => { runAsyncTask (() => onRejected (this .result )); }, }); } }); return p2; } catch (onRejected) { this .then (undefined , onRejected); } finally (onFinally ) { this .then (onFinally, onFinally); } }const resolvePromise = (x, p2, resolve, reject ) => { if (x === p2) { throw new TypeError ("Chaining cycle detected for MyPromise #<Promise>" ); } if (x instanceof MyPromise ) { x.then (res => resolve (res), err => reject (err)) } else { resolve (x); } };
16.手写 Promise.all 方法 传入的所有 Promise 都正常解决就返回一个 Promise,resolve 中的结果是所有包含所有解决promise结果的数组。如果有一个拒绝就立即返回一个拒绝的Promise
[!IMPORTANT]
Promise.resolve() 会返回一个 Promise,如果传入的值是 Promise,就返回这个 Promise
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 const MyPromiseAll = (promiseArr ) => { return new Promise ((resolve, reject ) => { if (!Array .isArray (promiseArr)) { return reject (new TypeError ('Arguments must be a Array' )) } let count = 0 let result = [] if (promiseArr.length === 0 ) { return resolve ([]) } for (let i = 0 ; i < promiseArr.length ; i++) { Promise .resolve (promiseArr[i]) .then (res => { result[i] = res count++ if (count === promiseArr.length ) { resolve (result) } }) .catch (err => { return reject (err) }) } }) }
17.手写 Promise.race 方法 哪个状态先确定,就返回它d
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 const MyPromiseRace = (promiseArr ) => { return new Promise ((resolve, reject ) => { if (!Array .isArray (promiseArr)) { return reject (new TypeError ('Arguments must be a Array' )) } if (promiseArr.length === 0 ) { return resolve ([]) } for (let i = 0 ; i < promiseArr.length ; i++) { Promise .resolve (promiseArr[i]) .then (res => { return resolve (res) }) .catch (err => { return reject (err) }) } }) }
18.手写 instanceof 用于检查某个对象是否为构造函数的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function MyInstanceof (obj, constructor ) { if (typeof obj !== 'object' || typeof obj === 'null' || typeof constructor !== 'function' ) { return false } let proto = Object .getPrototypeOf (obj) while (proto !== null ) { if (proto === constructor.prototype ) { return true } proto = Object .getPrototypeOf (proto) } return false }
19.函数柯里化 把一个能解收多个参数的函数转化为可以分步接收参数的函数。
如 fn(a, b, c) = fn(a, b)(c) = fn(a)(b)(c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function curry (fn ) { return function curried (...args ) { if (args.length >= fn.length ) { return fn.apply (this , args) } else { return (...args2 ) => { return curried.apply (this , args.concat (args2)) } } } }
20.将列表转为树 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 const listToTree = (list ) => { const tree = []; const nodeMap = new Map (); list.forEach ((item ) => { nodeMap.set (item.id , { ...item, children : [] }); }); list.forEach ((item ) => { if (item.parentId === 0 ) { tree.push (nodeMap.get (item.id )); } else { const parent = nodeMap.get (item.parentId ); if (parent) { parent.children .push (nodeMap.get (item.id )); } } }); return tree; };
21.将树转为列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const treeToList = (tree ) => { let res = [] const dfs = (nodes ) => { nodes.forEach (node => { if (node.children && node.children .length > 0 ) { dfs (node.children ) delete node.children } res.push (node) }); } dfs (Array .isArray (tree) ? tree : [tree]) return res }
22.将虚拟 DOM 转为真实 DOM 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 const render = (vNode ) => { if (typeof vNode === 'number' ) { vNode.toString () } if (typeof vNode === 'string' ) { const dom = document .createTextNode (vNode) return dom } const dom = document .createElement (vNode.tag ) if (vNode.attrs ) { for (let key in vNode.attrs ) { dom.setAttribute (key, vNode.attrs [key]) } } if (vNode.children .length > 0 ) { vNode.children .forEach (item => { dom.appendChild (render (item)) }) } return dom }
23.实现 LRU 缓存机制 最近最少使用
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 class LRUCache { constructor (capacity ) { this .capacity = capacity this .container = new Map () } get (key ) { if (this .container .has (key)) { const tempVal = this .container .get (key) this .container .delete (key) this .container .set (key, tempVal) return tempVal } else { return -1 } } put (key, value ) { if (this .container .has (key)) { this .container .delete (key) this .container .set (key, value) } else { if (this .container .size < this .capacity ) { this .container .set (key, value) } else { this .container .delete (this .container .keys ().next ().value ) this .container .set (key, value) } } } }
24.实现有并行限制的 Promise 调度器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 示例addTask (1000 ,"1 ");addTask (500 ,"2 ");addTask (300 ,"3 ");addTask (400 ,"4 "); 的输出顺序是:2 3 1 4 整个的完整执行流程: 一开始1 、2 两个任务开始执行500ms 时,2 任务执行完毕,输出2 ,任务3 开始执行800ms 时,3 任务执行完毕,输出3 ,任务4 开始执行1000ms 时,1 任务执行完毕,输出1 ,此时只剩下4 任务在执行1200ms 时,4 任务执行完毕,输出4
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 class Scheduler { constructor (limit ) { this .queue = [] this .maxCount = limit } add (time, res ) { const task = ( ) => { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (res) }, time) }) } this .queue .push (task) } start ( ) { for (let i = 0 ; i < this .maxCount ; i++) { this .request () } } request ( ) { if (this .queue .length <= 0 ) { return } this .queue .shift ()() .then (res => { console .log (res) this .request () }) } }
25.实现模板字符串的解析功能 1 2 3 4 5 6 7 8 const renderString = (template, data ) => { let computed = template.replace (/\{\{(\w+)\}\}/g , (match, key ) => { return data[key] }) return computed }
26.冒泡排序 每一轮都往末尾冒出一个相对最大的元素。时间复杂度 O ( n 2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const bubbleSort = (arr ) => { const len = arr.length for (let i = 0 ; i < len - 1 ; i++) { for (let j = 0 ; j < len - i - 1 ; j++) { if (arr[j] > arr[j + 1 ]) { [arr[j], arr[j + 1 ]] = [arr[j + 1 ], arr[j]] } } } return arr }
27.选择排序 每一次都选择最小的元素往前排 时间复杂度 O ( n 2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const selectSort = (arr ) => { let len = arr.length for (let i = 0 ; i < len - 1 ; i++) { let minIndex = i for (let j = minIndex; j < len; j++) { if (arr[j] < arr[minIndex]) { minIndex = j } } if (minIndex !== i) { [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]] } } return arr }
28.插入排序 时间复杂度 O ( n 2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const insertSort = (arr ) => { let len = arr.length for (let i = 1 ; i < len; i++) { let cur = arr[i] let j = i while (j > 0 && arr[j - 1 ] > cur) { arr[j] = arr[j - 1 ] j-- } arr[j] = cur } return arr }
29.快速排序 时间复杂度 O ( n log n )
1 2 3 4 5 6 7 8 9 10 11 const quickSort = (arr ) => { if (arr.length <= 1 ) return arr let base = arr[0 ] let left = arr.filter ((item, index ) => item <= base && index !== 0 ) let right = arr.filter (item => item > base) const result = [...quickSort (left), base, ...quickSort (right)] return result }
原地操作数组发方式
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 var sortArray = function (nums ) { const partition = function (arr, left, right ) { const pivotValue = arr[left]; let i = left; let j = right; while (i < j) { while (i < j && arr[j] >= pivotValue) { j--; } while (i < j && arr[i] <= pivotValue) { i++; } if (i < j) { [arr[i], arr[j]] = [arr[j], arr[i]]; } } [arr[left], arr[j]] = [arr[j], arr[left]]; return j; }; const quickSort = function (arr, left = 0 , right = arr.length - 1 ) { if (left >= right) return arr; const pivotIndex = partition (arr, left, right); quickSort (arr, left, pivotIndex - 1 ); quickSort (arr, pivotIndex + 1 , right); return arr; }; return quickSort (nums, 0 , nums.length - 1 ); };
30.二分归并排序 两部分排好,再依次比较
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 const merge = (left, right ) => { let res = [] let i = 0 , j = 0 while (i < left.length && j < right.length ) { if (left[i] < right[j]) { res.push (left[i]) i++ } else { res.push (right[j]) j++ } } if (i < left.length ) res.push (...left.slice (i)) else if (j < right.length ) res.push (...right.slice (j)) return res }const mergeSort = (arr ) => { if (arr.length <= 1 ) return arr let mid = Math .floor (arr.length / 2 ) let left = mergeSort (arr.slice (0 , mid)) let right = mergeSort (arr.slice (mid)) const result = merge (left, right) return result }
31.二分查找 确定元素在一个已经排好序的数组中的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const binarySearch = (arr, target, start, end ) => { if (end < start) return -1 let mid = Math .floor ((start + end) / 2 ) if (target === arr[mid]) return mid if (target < arr[mid]) { return binarySearch (arr, target, start, mid - 1 ) } else if (target > arr[mid]) { return binarySearch (arr, target, mid + 1 , end) } }
32.使用 XHR 实现 AJAX 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 const ajax = (options ) => { const { url, method = 'GET' , data = null , headers = {}, onSuccess, onError } = options const xhr = new XMLHttpRequest () xhr.open (method, url, true ) for (const key in headers) { xhr.setRequestHeader (key, headers[key]) } xhr.onreadystatechange = () => { if (xhr.readyState === 4 ) { if (xhr.status >= 200 && xhr.status < 300 ) { onSuccess && onSuccess (xhr.responseText ) } else { onError && onError (xhr.status , xhr.responseText ) } } } xhr.onerror = () => { onError && onError (0 , 'Network Error' ) } xhr.send () }
33.对象扁平化 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 const flattenObject = (obj, parentKey = '' , result = {} ) => { for (let key in obj) { let newKey = parentKey ? `${parentKey} .${key} ` : key if (typeof obj[key] === 'object' && obj[key] !== null ) { if (Array .isArray (obj[key])) { obj[key].forEach ((item, index ) => { let arrayKey = `${newKey} [${index} ]` if (typeof item === 'object' && item !== null ) { flattenObject (item, arrayKey, result) } else { result[arrayKey] = item } }) } else { flattenObject (obj[key], newKey, result) } } else { result[newKey] = obj[key] } } return result }
使用示例:
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 // ------------------- 测试代码 ------------------- const obj = { a: { b: 1 , c: 2 , d: { e: 5 } }, b: [1 , 3 , { a: 2 , b: 3 }], c: 3 } const res = flattenObject(obj) console.log(res)// {// 'a.b' : 1 ,// 'a.c' : 2 ,// 'a.d.e' : 5 ,// 'b[0]' : 1 ,// 'b[1]' : 3 ,// 'b[2].a' : 2 ,// 'b[2].b' : 3 // c: 3 // }
34.实现 Object.is Object.is不会转换被比较的两个值的类型,这点和===更为相似,他们之间也存在一些区别。 1. NaN 和 NaN 在===中是不相等的,而在Object.is中是相等的 2. +0和-0在===中是相等的,而在Object.is中是不相等的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Object .MyIs = (x, y ) => { if (x === y) { if (x === 0 || y === 0 ) { return 1 / x === 1 / y } return true } else { if (x !== x && y !== x) { return true } return false } }
35.类数组转为数组的方法 1 2 3 4 5 6 7 8 9 function arrayLikeToArray (arrayLike ) { return Array .from (arrayLike); }function arrayLikeToArray (arrayLike ) { return [...arrayLike]; }
36.实现大数相加 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const addBigNumbers = (num1, num2 ) => { const max = Math .max (num1.length , num2.length ) num1 = num1.padStart (max, '0' ) num2 = num2.padStart (max, '0' ) let carry = 0 let result = '' for (let i = max - 1 ; i >= 0 ; i--) { let sum = Number (num1[i]) + Number (num2[i]) + carry carry = Math .floor (sum / 10 ) result = (sum % 10 ) + result } if (carry !== 0 ) { result = carry + result } return result }
37.多行文本溢出省略 1 2 3 4 5 6 7 8 .multiline-ellipsis { display : -webkit-box; -webkit-line -clamp: 3 ; -webkit-box-orient : vertical; overflow : hidden; text-overflow : ellipsis; }
38.数组乱序 1 2 3 4 const randomArr = (nums ) => { return nums.sort (() => Math .random () - 0.5 ) }
39.手写 JSONP 1 2 3 4 5 6 7 8 9 10 11 12 const addScript = (url ) => { const script = document .createElement ('script' ) script.src = url script.type = 'text/javascript' document .body .appendChild (script) }addScript ("http://xxx.xxx.com/xxx.js?callback=handleRes" );const handleRes = (data ) => { console .log (data) }
40.手写事件委托 1 2 3 4 5 6 7 8 9 10 const eventDelegate = (parent, eventType, target, callback ) => { parent.addEventListener (eventType, (event ) => { if (event.target .matches (target)) { callback (event) } }) }
41.手写 sleep 函数 1 2 3 4 5 6 7 8 9 10 11 12 13 const MySleep = (delay ) => { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve () }, delay) }) }const test = async ( ) => { console .log ('开始' ) await MySleep (3000 ) console .log ('结束' ) }
42.异步循环打印 1 2 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const sleepToPrint = (time, val ) => { return new Promise (resolve => { setTimeout (() => { resolve (val) }, time) }) }const toPrint_123 = async ( ) => { for (let i = 1 ; i <= 3 ; i++) { const res = await sleepToPrint (1000 , i) console .log (res) } }toPrint_123 ()
43.异步循环打印 红 黄 绿 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const asyncColor = (time, color ) => { return new Promise (resolve => { setTimeout (() => { console .log (color) resolve (color) }, time) }) }const startTask = async ( ) => { await asyncColor (1000 , '红' ) await asyncColor (2000 , '黄' ) await asyncColor (3000 , '绿' ) startTask () }startTask ()
44.手写数组 filter 方法 1 2 3 4 5 6 7 8 9 10 Array .prototype .MyFilter = function (callback ) { const result = [] for (let i = 0 ; i < this .length ; i++) { if (callback (this [i], i , this )) { result.push (this [i]) } } return result }
45.手写数组 reduce 方法 对数组中的元素执行一个累加器的操作,最后返回累计结果
1 2 3 4 5 6 7 8 9 Array .prototype .MyReduce = function (callback, initialValue ) { let pre = initialValue !== undefined ? initialValue : this [0 ] let start = initialValue !== undefined ? 0 : 1 for (let i = start; i < this .length ; i++) { pre = callback (pre, this [i], i, this ) } return pre }
46.手写数组 map 方法 对数组中每个元素执行特定操作,返回新的数组
1 2 3 4 5 6 7 Array .prototype .MyMap = function (callback ) { let result = [] for (let i = 0 ; i < this .length ; i++) { result[i] = callback (this [i], i, this ) } return result }
47.手写数组 fill 方法 使用一个值填充数组起始到终止索引的元素
1 2 3 4 5 6 7 Array .prototype .MyFill = function (val, start, end ) { for (let i = start; i < end; i++) { this [i] = val } return this }
48.图片懒加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const imgs = document .querySelectorAll ('#imgs-need-to-lazy-load img' ); const len = imgs.length ;const viewHeight = window .innerHeight || document .documentElement .clientHeight ; let index = 0 ; const lazyLoad = ( ) => { for (let i = index; i < len; i++) { const top = imgs[i].getBoundingClientRect ().top ; if (top < viewHeight) { imgs[i].src = imgs[i].getAttribute ('data-src' ); index = i + 1 ; } } };lazyLoad ();window .addEventListener ('scroll' , lazyLoad, false );
49.驼峰转下划线 1 2 3 4 const toLine = (str ) => { return str.replace (/([A-Z])/g , (match ) => `_${match} ` .toLowerCase ()) }
50.下划线转驼峰 1 2 3 4 const toHump = (str ) => { return str.replace (/\_(\w)/g , (match, letter ) => letter.toUpperCase ()) }
51.实现千分位分割 直接调 API
1 2 3 4 number.toLocaleString ()
手写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const formatNumber = (num ) => { const nums = num.toString () let result = '' let count = 0 for (let i = nums.length - 1 ; i >= 0 ; i--) { count++ result = nums[i] + result if (count % 3 === 0 && i !== 0 ) { result = ',' + result } } return result }
52.手写 Object.create
1 2 3 4 5 6 7 8 9 10 11 12 Object .MyCreate = (proto, properties ) => { function F ( ) {} F.prototype = proto const obj = new F () if (properties) { Object .defineProperties (obj, properties) } return obj }
53.手写 Promise.any
接收一个 Promise 数组
返回第一个成功的 Promise
如果都不成功,返回’all promise rejected’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const MyPromiseAny = (promiseArr ) => { if (!Array .isArray (promiseArr)) { throw new TypeError ('Arguments must be a Array' ) } return new Promise ((resolve, reject ) => { let count = 0 for (let i = 0 ; i < promiseArr.length ; i++) { Promise .resolve (promiseArr[i]) .then (res => { resolve (res) }) .catch (() => { count++ if (count === promiseArr.length ) { reject ('All promise rejected' ) } }) } }) }
54.手写 JSONP 1 2 3 4 5 6 7 8 9 10 11 12 const addScript = (src ) => { const script = document .createElement ('script' ) script.src = src ducument.body .appenChild (script) }addScript ('http://www.xxx.com?callback=handleRes' )const handleRes = (data ) => { con }
55.扁平对象转嵌套对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const transformObj = (obj ) => { let result = {} for (const key in obj) { if (obj.hasOwnProperty (key)) { let cur = result const parts = key.split ('.' ) for (let i = 0 ; i < parts.length ; i++) { if (i === parts.length - 1 ) { cur[parts[i]] = obj[key] } else { if (!cur[parts[i]]) cur[parts[i]] = {} cur = cur[parts[i]] } } } } return result }
1 2 3 4 5 6 7 8 9 10 const entry = { 'a.b.c.dd' : 'abcdd' , 'a.d.xx' : 'adxx' , 'a.e' : 'ae' , };const output = transformEntry (entry);console .log (output);
56.解析 URL 参数为对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const paramsToObject = (url ) => { const queryString = url.split ('?' )[1 ] const params = queryString.split ('&' ) let paramObj = {} for (let i = 1 ; i < params.length ; i++) { let [key, val] = [params[i].split ('=' )[0 ], params[i].split ('=' )[1 ]] paramObj[decodeURLComponent (key)] = decodeURLComponent (val) } return paramObj }
57.数组乱序(洗牌算法) 1 2 3 4 5 6 7 8 9 10 11 function shuffle (array ) { for (let i = array.length - 1 ; i > 0 ; i--) { const j = Math .floor (Math .random () * (i + 1 )); [array[i], array[j]] = [array[j], array[i]]; } return array; }
58.虚拟 DOM 结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const VNode = { type :'div' , props : { className : ['header' , 'navigation' ], style : { color : 'red' , } }, children :[ { type :'input' , props : { classNmae :['my-input' ], placeholder :'请输入内容...' } }, {....}, '文本节点' ] }
59.对象比较 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 const isEqual = (obj1, obj2 ) => { if (obj1 === obj2) return true if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null ) { return false } const keys1 = Object .keys (obj1) const keys2 = Object .keys (obj2) if (keys1.length !== keys2.length ) return false let result = true for (let key of keys1) { if (keys2.includes (key)) { if (!isEqual (obj1[key], obj2[key])) { result = false } } else { result = false } } return result }
60.取出括号中的字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const getStrFromMeth = (str ) => { let stack = [] let result = [] for (let i = 0 ; i < str.length ; i++) { if (str[i] === '(' ) { stack.push (i) } else if (str[i] === ')' ) { if (stack.length !== 0 ) { let start = stack.pop () result.push (str.slice (start + 1 , i)) } } } return result }const str = '(a+b)*(c+a*(d+e))' console .log (getStrFromMeth (str))
61.实现 safeGet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const safeGet = (obj, path ) => { let keys = path.split ('.' ) let inObj = obj for (let i = 0 ; i < keys.length ; i++) { let target = inObj[keys[i]] if (!target) return undefined if (i === keys.length - 1 ) return target inObj = inObj[keys[i]] } }const obj = { a : { b : { c : 42 } } };console .log (safeGet (obj, 'a.b' )); console .log (safeGet (obj, 'a.b.x' ));
62.使用 Node.js 写一个接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const express = require ('express' )const app = express () app.use (express.json ()) app.get ('/api/data' , (req, res ) => { const data = req.body res.json ({message :'data received' , data}) }) app.use ((req, res ) => { res.status (404 ).json ({error :'NOT Found' }) }) app.listen (3000 , ()=> { console .log ('服务器已启动' ) })
63.手写 HTML 空白页面 1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-sacle=1.0" > <style > </style > </head > <body > <h1 > </h1 > </body > </html >
64.手写 fetch 请求超时处理 1 2 3 4 5 6 7 8 const fetchTimeout = (url, options, timeout ) => { const timeGuard = new Promise ((resolve, reject ) => { setTimeout (reject (new Error ('request time out!' )), timeout) }) const fetchPromise = fetch (url, options) return Promise .race (timeGuard, fetchPromise) }
65.手写数组 concat 方法 1 2 3 4 Array .prototype .myConcat = function (args ) { return [...this , ...args] }
66.手写数组 sort 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Array .prototype .sort = function (callback ) { const quickSort = (arr ) => { let mid = arr[0 ] let left = [] let right = [] let left = arr.filter ((item, index ) => { return index !== 0 && callback (item, mid)) < 0 }) let left = arr.filter ((item, index ) => { return index !== 0 && callback (item, mid)) >= 0 }) return [...quickSort (left), mid, ...quickSort (right)] } const curArr = quickSort (this ) for (let i = 0 ; i < this .length ; i++) { this [i] = curArr[i] } }
67.奇数次调用打印1,偶数次调用打印2 1 2 3 4 5 6 7 8 9 10 11 12 function func ( ) { let count = 1 function ( ) { if (count % 2 === 1 ) { console .log (1 ) } else { console .log (2 ) } count++ }() }
68.实现 lazyMan 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 class lazyMan { constructor ( ) { this .queue = [] setTimeout (() => { this .next () }, 0 ) } eat ( ) { this .queue .push (() => { console .log ('eat' ) this .next () }) return this } sleep (time ) { this .queue .push (() => { setTimeout (() => { console .log ('sleep' ) this .next () }, time) }) } work ( ) { this .queue .push (() => { console .log ('work' ) this .next () }) return this } next ( ) { const task = this .queue .shift () if (task) { task () } } }const obj = new lazyMan (); obj.eat ().sleep (5000 ).work ();
69.实现字符串的 trim 方法 1 2 3 4 5 6 7 8 9 10 String .prototype .myTrim = function ( ) { let start = 0 let end = this .length - 1 while (start <= end && this [start] === ' ' ) start++ while (start <= end && this [end] === ' ' ) end-- return this .slice (start, end + 1 ) }
70.事件缓存器 第一次的时候计算然后缓存结果,第二次的时候直接从缓存中取结果,不用重复结算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function cache (func ) { let map = new Map () return function (...args ) { let key = args.join ('' ) if (map.has (key)) { return map.get (key) } else { const result = func.apply (this , args) map.set (key, result) return result } } }function add (a, b ) { return a + b; }const addCache = cache (add);console .log (addCache (1 , 2 )); console .log (addCache (1 , 2 )); console .log (addCache (1 , 3 )); console .log (addCache (1 , 3 ));
71.链式调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function sum (value ) { let total = value; function f (nextValue ) { total += nextValue; return f; } f.value = function ( ) { return total; }; return f; }console .log (sum (1 )(2 )(3 ).value ());
72.检查数字中是否有重复的元素 注意数组中的元素大小值为 0 ~(n-1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const isUniqueArr = (nums ) => { for (let i = 0 ; i < nums.length ; i++) { let cur = Math .abs (nums[i]) if (nums[cur] < 0 ) { return false } else { if (cur === 0 ) { nums[cur] = -n } else { nums[cur] = - -nums[cur] } } } }