以下是纯干货总结的手写版笔试题代码,拿走不谢!
Promise 变体 promise.first
第一个返回成功的 Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if (!Promise .first ) { Promise .first = (promiseList ) => { return new Promise ((resolve, reject ) => { let rejectNum = 0 ; const len = promiseList.length ; promiseList.forEach ((pr ) => { Promise .resolve (pr) .then (resolve) .catch (() => { rejectNum++; if (rejectNum == len) { reject ("all promise is reject" ); } }); }); }); }; }
promise.last
最后一个返回成功的 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 if (!Promise .last ) { Promise .last = (promiseList ) => { return new Promise ((resolve, reject ) => { let num = 0 ; const len = promiseList.length ; let lastResult; const fn = ( ) => { if (num !== len) { return ; } lastResult ? resolve (lastResult) : reject ("all promise is reject" ); }; promiseList.forEach ((pr ) => { Promise .resolve (pr) .then ((data ) => { lastResult = data; num++; fn (); }) .catch (() => { num++; fn (); }); }); }); }; }
promise.none
与 Promise.all 相反,所有的 promise 都被拒绝,则 Promise.none 变成完成状态
1 2 3 4 5 6 7 8 9 10 11 if (!Promise .none ) { Promise .none = (promiseList ) => { return Promise .all ( promiseList.map ((pr ) => { return new Promise ((resolve, reject ) => { return Promise .resolve (pr).then (reject, resolve); }); }) ); }; }
Promise.any
表示只获取所有的 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 if (!Promise .any ) { Promise .any = (promiseList ) => { return new Promise ((resolve, reject ) => { const result = []; let num = 0 ; const len = promiseList.length ; const fn = ( ) => { if (num == len) { result.length < 1 ? reject ("all promise is reject" ) : resolve (result); } }; promiseList.forEach ((pr ) => { Promise .resolve (pr) .then ((data ) => { result.push (data); num++; fn (); }) .catch (() => { num++; fn (); }); }); }); }; }
Promise.every
所有 promise 都进入完成状态,则返回 true,否则返回 false
1 2 3 4 5 6 7 8 9 if (!Promise .every ) { Promise .every = (promiseList ) => { return new Promise ((resolve, reject ) => { Promise .all (promiseList) .then (() => Promise .resolve (true )) .catch (() => Promise .reject (false )); }); }; }
Promisify 题意 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fs.readFile ("./index.js" , (err, data ) => { if (!err) { console .log (data.toString ()); } console .log (err); }); const readFile = promisify (fs.readFile );readFile ("./index.js" ) .then ((data ) => { console .log (data.toString ()); }) .catch ((err ) => { console .log ("error:" , err); });
解法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function promisify (fn ) { return (...args ) => { return new Promise ((resolve, reject ) => { fn.apply (this , [ ...args, (err, data ) => { if (!err) { return resolve (data); } return reject (err); }, ]); }); }; }
模拟实现 sleep 1 2 3 4 5 async function sleep (sleepTime ) { return new Promise ((resolve ) => { setTimeout (resolve, sleepTime); }); }
JS 实现一个带有并发限制的异步调度器 题意 JS 实现一个带有并发限制的异步调度器 Scheduler,保证同时运行的任务最多有十个,完善代码中 Scheduler 类:
1 2 3 4 5 6 7 8 9 Class Scheduler { constructor (max ){} run (callback ){} } let s = new Scheduler (10 );s.run (async () => { });
解法 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 Class Scheduler { COUNT = 0 ; LIST = []; constructor (max ){ this .LIMIT = 10 ; } async run (callback ){ if (this .COUNT >= this .LIMIT ){ await new Promise ((resolve ) => { this .list .push (resolve); }); } this .COUNT ++; const result = await callback (); this .COUNT --; if (this .list .length > 0 ){ this .list .shift ()(); } return result; } } let s = new Scheduler (10 );s.run (async () => { }); s.run (async () => { }); s.run (async () => { });
手写 Promise Promise 要点
Promise 是一个类,传入的参数是一个执行器,会立即执行
Promise 有三种状态
pending 等待
fulfilled 完成
rejected 失败
状态只能改变一次,不能逆向。
pending -> fulfilled
pending -> rejected
Promise 使用 resolve 和 reject 两个函数来改变状态
then 方法内部做状态判断,执行对应的方法
有静态方法 Promise.resolve 和 Promise.reject
手写版 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 122 123 124 125 126 127 128 const PENDING = "pending" ;const FULFILLED = "fulfilled" ;const REJECTED = "rejected" ;class Promise { state = PENDING ; value = undefined ; reason = undefined ; onResolveCallback = []; onRejectCallback = []; constructor (executor ) { try { executor (this .resolve , this .reject ); } catch (err) { this .reject (err); } } resolve (data ) { if (this .state !== PENDING ) { return ; } this .state = FULFILLED ; this .value = data; while (this .onResolveCallback .length > 0 ) { const currentResolve = this .onResolveCallback .shift (); currentResolve (this .value ); } } reject (err ) { if (this .state !== PENDING ) { return ; } this .state = REJECTED ; this .reason = err; while (this .onRejectCallback .length > 0 ) { const currentReject = this .onRejectCallback .shift (); currentReject (this .reason ); } } static resolve (param ) { if (param instanceof Promise ) { return param; } return new Promise ((resolve ) => { resolve (param); }); } static reject (reason ) { return new Promise ((resolve, reject ) => { reject (reason); }); } catch (fn) { if (this .state == REJECTED ) { typeof fn == "function" && fn (this .reason ); } } then (resolve, reject ) { const realResolve = typeof resolve == "function" ? resolve : (value ) => value; const realReject = typeof reject == "function" ? reject : (reason ) => { throw reason; }; const newPromise = new Promise ((resolve, reject ) => { const microResolve = ( ) => { queueMicrotask (() => { try { const x = realResolve (this .value ); resolvePromise (newPromise, x, resolve, reject); } catch (err) { reject (err); } }); }; const microReject = ( ) => { queueMicrotask (() => { try { const x = realReject (this .reason ); resolvePromise (newPromise, x, reasolve, reject); } catch (err) { reject (err); } }); }; if (this .state == FULFILLED ) { return microResolve (this .value ); } if (this .state == REJECTED ) { return microReject (this .reason ); } if (this .state == PENDING ) { this .onResolveCallback .push (microResolve); this .onRejectCallback .push (microReject); } }); return newPromise; } } function resolvePromise (newPromise, x, resolve, reject ) { if (newPromise == x) { return reject (new Error ("循环引用" )); } if (x instanceof Promise ) { x.then (resolve, reject); } else { resolve (x); } }
模拟 bind 的实现 ES5 版本 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 Function .prototype .bind = Function .prototype .bind || function (context ) { if (typeof this !== "function" ) { throw new Error ("can't bind" ); } let self = this ; let args = Array .prototype .slice (arguments , 1 ); const fnOP = function ( ) {}; const bound = function ( ) { let innerArgs = Array .prototype .slice (arguments ); return self.apply ( this instanceof fnOP ? this : context, args.concat (innerArgs) ); }; fnOP.prototype = this .prototype ; bound.prototype = new fnOP (); return bound; };
ES6 版本 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 Function .prototype .bind = Function .prototype .bind || function ( ) { if (typeof this !== "function" ) { throw new Error ("can't bind" ); } const args = Array .from (arguments ); const context = args.shift (); const self = this ; const fnOP = Object .create (this .prototype ); const fn = function ( ) { const innerArgs = Array .from (arguments ); return self.apply ( this instanceof fnOP ? this : context, args.concat (innerArgs) ); }; fn.prototype = fnOP; return fn; };
模拟 new 的实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function Otaku (name, age ) { this .name = name; this .age = age; this .habit = "Games" ; } Otaku .prototype .strength = 60 ;Otaku .prototype .sayYourName = function ( ) { console .log ("I am " + this .name ); }; myNew (Otaku , "Kevin" , "18" );console .log (person.name ); console .log (person.habit ); console .log (person.strength ); person.sayYourName ();
1 2 3 4 5 6 7 8 function myNew (fn, ...args ) { const obj = Object .create (fn.prototype ); const ret = fn.apply (obj, args); return typeof ret === "object" ? ret || obj : obj; }
深拷贝 深拷贝注意点
注意类型,7 种基本类型: boolean
、string
、number
、undefined
、null
、Symbol
、BigInt
Object
类型的可能情况(Object.prototype.toString.call):
[object Object]
[object Function] - typeof function == function
[object Null]
[object Array]
[object RegExp]
解法 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 function deepCopy (data, hash = new WeakMap () ) { if (typeof data !== "object" || data == null ) { return data; } if (hash.has (data)) { return hash.get (data); } const newData = Array .isArray (data) ? [] : {}; const dataKeys = Object .keys (data); dataKeys.forEach ((key ) => { const current = data[key]; const typeString = Object .prototype .toString .call (current).toLowerCase (); if (typeof current !== "object" || current == null ) { newData[key] = current; return ; } switch (typeString) { case "[object array]" : newData[key] = [...deepCopy (current, hash)]; break ; case "[object set]" : newData[key] = new Set ([...current.values ()]); break ; case "[object map]" : newData[key] = new Map ([...current]); break ; default : hash.set (data, data); newData[key] = deepCopy (current, hash); } }); return newData; }
数组去重 ES6 Set 1 2 const uniqueArr = [...new Set (arr)];const uniqueArr = Array .from (new Set (arr));
reduce 1 2 3 4 5 6 7 8 9 function unique (arr ) { return arr.sort ().reduce ((acc, current ) => { if (acc.length == 0 || acc[acc.length - 1 ] !== current) { acc.push (current); } return acc; }, []); }
filter 1 2 3 4 5 function unique (arr ) { return arr.filter ((element, index, array ) => { return array.indexOf (element) == index; }); }
扁平化数组 使用 reduce 方法进行扁平化 1 2 3 4 5 6 7 function flattenDeep (arr ) { return Array .isArray (arr) ? arr.reduce ((acc, current ) => { return [...acc, ...flattenDeep (current)]; }, []) : [arr]; }
模拟栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function flattenDeep (arr ) { const stack = [...arr]; const result = []; while (stack.length ) { const current = stack.pop (); if (Array .isArray (current)) { stack.push (...current); } else { result.unshift (current); } } return result; }
柯里化 1 2 3 4 5 6 7 8 9 10 11 12 function curry (fn ) { return function curried (...args ) { if (args.length >= fn.length ) { fn.apply (this , args); } else { return function (...args2 ) { return curried.apply (this , args.concat (args2)); }; } }; }
防抖 1 2 3 4 5 6 7 8 9 function debounce (fn, wait ) { let timer = null ; return () => { clearTimeout (timer); timer = setTimeout (() => { fn.apply (this , Array .from (arguments )); }, wait); }; }
节流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function throttle (fn, wait ) { let timer = null ; return () => { if (timer !== null ) { return ; } timer = setTimeout (() => { fn.apply (this .Array .from (arguments )); timer = null ; }, wait); }; }