面試會(huì)遇到的手寫 Pollyfill 都在這里了
最近會(huì)把前陣子自己復(fù)盤歸類整理的這次跳槽面試遇到的所有題目發(fā)布到公眾號(hào),這是第一篇。不要驚訝,上次跳槽考的也基本是這些題目,時(shí)間長(zhǎng)了會(huì)忘,你只是需要一個(gè)清單!
new
測(cè)試用例:
function?Fn?(name)?{
??this.name?=?name
}
console.log(myNew(Fn('lulu')))
實(shí)現(xiàn):
function?myNew?()?{
??const?obj?=?{}
??const?Fn?=?Array.prototype.shift.call(arguments)
??//?eslint-disable-next-line?no-proto
??obj.__proto__?=?Fn.prototype
??const?returnVal?=?Fn.apply(obj,?arguments)
??return?typeof?returnVal?===?'object'???returnVal?:?obj
}
bind
測(cè)試用例:
this.x?=?9
const?obj?=?{
??x:?81,
??getX:?function?()?{
????return?this.x
??}
}
console.log(obj.getX())?//?81
const?retrieveX?=?obj.getX
console.log(retrieveX())?//?9
const?boundGetX?=?retrieveX.mybind(obj)
console.log(boundGetX())?//?81
實(shí)現(xiàn):
Function.prototype.mybind?=?function?()?{
??const?outerArgs?=?Array.from(arguments)
??const?ctx?=?outerArgs.shift()
??const?self?=?this
??return?function?()?{
????const?innerArgs?=?Array.from(arguments)
????return?self.apply(ctx,?[...outerArgs,?...innerArgs])
??}
}
instanceof
測(cè)試用例:
console.log(myInstanceof("111",?String));?//false
console.log(myInstanceof(new?String("111"),?String));//true
實(shí)現(xiàn):
function?myInstanceof(left,?right)?{
????//基本數(shù)據(jù)類型直接返回false
????if(typeof?left?!==?'object'?||?left?===?null)?return?false;
????//getProtypeOf是Object對(duì)象自帶的一個(gè)方法,能夠拿到參數(shù)的原型對(duì)象
????let?proto?=?Object.getPrototypeOf(left);
????while(true)?{
????????//查找到盡頭,還沒(méi)找到
????????if(proto?==?null)?return?false;
????????//找到相同的原型對(duì)象
????????if(proto?==?right.prototype)?return?true;
????????proto?=?Object.getPrototypeOf(proto);
????}
}
debounce
在規(guī)定時(shí)間內(nèi)函數(shù)只會(huì)觸發(fā)一次,如果再次觸發(fā),會(huì)重新計(jì)算時(shí)間。
/***?
?*?@description?防抖函數(shù)
?*?@param?func?函數(shù)
?*?@param?wait?延遲執(zhí)行毫秒數(shù)
?*?@param?immediate?是否立即執(zhí)行
?*?*/
function?debouncing(func,?wait?=?1000,?immediate?=?true)?{
????let?timer?=?null;
????return?function?()?{
????????let?args?=?arguments;
????????let?context?=?this;
????????if?(timer)?{
????????????clearTimeout(timer);
????????}
????????if?(!immediate)?{
????????????//第一種:n秒之后執(zhí)行,n秒內(nèi)再次觸發(fā)會(huì)重新計(jì)算時(shí)間
????????????timer?=?setTimeout(()?=>?{
????????????????//確保this指向不會(huì)改變
????????????????func.apply(context,?[...args]);
????????????},?wait);
????????}?else?{
????????????//第二種:立即執(zhí)行,n秒內(nèi)不可再次觸發(fā)
????????????let?callnew?=?!timer;
????????????timer?=?setTimeout(()?=>?{
????????????????timer?=?null;
????????????????console.log('kaka')
????????????},?wait);
????????????if?(callnew)?func.apply(context,?[...args])
????????}
????}
}
function?fn()?{
????console.log('debluncing')
}
let?f1?=?debouncing(fn,?1000);
setInterval(()?=>?{
????f1()
},?1000);
throttle
節(jié)流指的是函數(shù)一定時(shí)間內(nèi)不會(huì)再次執(zhí)行,用作稀釋函數(shù)的執(zhí)行頻率。
/**
?*?@description?節(jié)流函數(shù)
?*?@param?func?函數(shù)
?*?@param?wait?延遲執(zhí)行毫秒數(shù)
?*?@param type 1:時(shí)間戳版本 2:?定時(shí)器版本
?*??*/
function?throttle(func,?wait?=?1000,?type?=?1)?{
????if?(type?===?1)?{
????????let?timeout?=?null;
????????return?function?()?{
????????????const?context?=?this;
????????????const?args?=?arguments;
????????????if?(!timeout)?{
????????????????timeout?=?setTimeout(()?=>?{
????????????????????timeout?=?null;
????????????????????func.apply(context,?[...args]);
????????????????},?wait);
????????????}
????????}
????}?else?{
????????let?previous?=?0;
????????return?function?()?{
????????????const?context?=?this;
????????????const?args?=?arguments;
????????????let?newDate?=?new?Date().getTime();
????????????if?(newDate?-?previous?>?wait)?{
????????????????func.apply(context,?[...args]);
????????????????previous?=?newDate;
????????????}
????????}
????}
}
function?fn()?{
????console.log('throttle')
}
const?f1?=?throttle(fn);
setInterval(()?=>?{
????f1()
},?100);
deepClone
測(cè)試用例:
const?map?=?new?Map()
map.set('key',?'value')
map.set('name',?'kaka')
const?set?=?new?Set()
set.add('11').add('12')
const?target?=?{
??field1:?1,
??field2:?undefined,
??field3:?{
????child:?'child'
??},
??field4:?[
????2,?8
??],
??empty:?null,
??map,
??set
}
target.target?=?target
const?target1?=?deepClone(target)
target1.a?=?'a'
console.log('?',?target)
console.log('?',?target1)
實(shí)現(xiàn):
//?判斷類型
function?getType?(target)?{
??return?Object.prototype.toString.call(target).slice(8,?-1)
}
//?判斷是否是原始類型類型.
//?對(duì)應(yīng)可引用的數(shù)據(jù)類型,需要遞歸遍歷;對(duì)應(yīng)不可引用的數(shù)據(jù)類型,直接復(fù)制即可
function?isReferenceType?(target)?{
??let?type?=?typeof?target
??return?(target?!==?null?&&?(type?===?'object'?||?type?===?'function'))
}
//?獲取原型上的方法
function?getInit?(target)?{
??let?ClassNames?=?target.constructor
??return?new?ClassNames()
}
//?引用類型
const?mapTag?=?'Map'
const?setTag?=?'Set'
const?arrayTag?=?'Array'
const?objectTag?=?'Object'
//?不可引用類型
const?boolTag?=?'Boolean'
const?dateTag?=?'Date'
const?errorTag?=?'Error'
const?numberTag?=?'Number'
const?regexpTag?=?'RegExp'
const?stringTag?=?'String'
const?symbolTag?=?'Symbol'
const?bufferTag?=?'Uint8Array'
let?deepTag?=?[mapTag,?setTag,?arrayTag,?objectTag]
function?deepClone?(target,?map?=?new?WeakMap())?{
??let?type?=?getType(target)
??let?isOriginType?=?isReferenceType(target)
??if?(!isOriginType)?{?return?target?}?//?對(duì)于不可引用的數(shù)據(jù)類型,直接復(fù)制即可
??let?cloneTarget
??if?(deepTag.includes(type))?{
????cloneTarget?=?getInit(target)
??}
??//?防止循環(huán)引用
??if?(map.get(target))?{
????return?map.get(target)
??}
??map.set(target,?cloneTarget)
??//?如果是?mapTag?類型
??if?(type?===?mapTag)?{
????console.log(target,?cloneTarget,?'target')
????target.forEach((v,?key)?=>?{
??????cloneTarget.set(key,?deepClone(v,?map))
????})
????return?cloneTarget
??}
??//?如果是?setTag?類型
??if?(type?===?setTag)?{
????target.forEach((v)?=>?{
??????cloneTarget.add(deepClone(v,?map))
????})
????return?cloneTarget
??}
??//?如果是?arrayTag?類型
??if?(type?===?arrayTag)?{
????target.forEach((v,?i)?=>?{
??????cloneTarget[i]?=?deepClone(v,?map)
????})
????return?cloneTarget
??}
??//?如果是?objectTag?類型
??if?(type?===?objectTag)?{
????let?array?=?Object.keys(target)
????array.forEach((i,?v)?=>?{
??????cloneTarget[i]?=?deepClone(target[i],?map)
????})
????return?cloneTarget
??}
}
reduce
測(cè)試用例:
console.log([1,?2,?3,?4].myReduce((total,?cur)?=>?total?+?cur,?0))
實(shí)現(xiàn):
/*?eslint-disable?no-extend-native?*/
Array.prototype.myReduce?=?function?(callback,?initialVal)?{
??const?arr?=?this
??let?base?=?initialVal?==?null???0?:?initialVal
??let?startPoint?=?initialVal?==?null???0?:?1
??for?(let?i?=?0;?i?????base?=?callback(base,?arr[i],?i?+?startPoint,?arr)
??}
??return?base
}
promise
//?Promise?是一個(gè)可以?new?的類
class?Promise?{
??constructor?(executor)?{
????this.status?=?'PENDING'?//?promise?默認(rèn)是pending態(tài)
????this.reason?=?this.val?=?undefined?//?val?用于儲(chǔ)存?resolve?函數(shù)的參數(shù),reason?用于儲(chǔ)存?reject?函數(shù)的參數(shù)
????/**
?????????*?這里用數(shù)組進(jìn)行回調(diào)函數(shù)的存儲(chǔ)?是因?yàn)橐环N場(chǎng)景,即同一個(gè)?promisee?多次調(diào)用?then?函數(shù)
?????????*?let?p?=?new?Promise((resolve)=>resolve())
?????????*?p.then()...
?????????*?p.then()...
?????????*?p.then()...
?????????*?這里數(shù)組就應(yīng)儲(chǔ)存三個(gè)函數(shù)?當(dāng)狀態(tài)從?pending?改變時(shí),數(shù)組遍歷執(zhí)行
?????????*??*/
????this.onResolvedCallbacks?=?[]
????this.onRejectedCallbacks?=?[]
????const?resolve?=?(val)?=>?{
??????//?如果一個(gè)promise?resolve?了一個(gè)新的?promise
??????//?則會(huì)等到內(nèi)部的?promise?執(zhí)行完,?獲取它返回的結(jié)果
??????if?(val?instanceof?Promise)?{
????????return?val.then(resolve,?reject)
??????}
??????//?這里必須進(jìn)行一次狀態(tài)判斷,?因?yàn)橐粋€(gè)?promise?只能變一次狀態(tài)
??????//?當(dāng)在調(diào)用?resolve?之前調(diào)用了?reject,?則?status?已經(jīng)改變,這里應(yīng)不再執(zhí)行
??????if?(this.status?===?'PENDING')?{
????????this.status?=?'FULLFILLD'
????????this.val?=?val
????????this.onResolvedCallbacks.forEach(cb?=>?cb())
??????}
????}
????const?reject?=?(reason)?=>?{
??????//?如果是?reject?的,?不用考慮?reason?是不是?promise?了,直接錯(cuò)誤跑出
??????if?(this.status?===?'PENDING')?{
????????this.status?=?'REJECTED'
????????this.reason?=?reason
????????this.onRejectedCallbacks.forEach(cb?=>?cb())
??????}
????}
????//?promise?必定會(huì)執(zhí)行函數(shù)參數(shù),?也算是一個(gè)缺點(diǎn)
????try?{
??????executor(resolve,?reject)
????}?catch?(e)?{
??????reject(e)
????}
??}
??static?resolve?(value)?{
????return?new?Promise((resolve,?reject)?=>?{
??????resolve(value)
????})
??}
??static?reject?(reason)?{
????return?new?Promise((resolve,?reject)?=>?{
??????reject(reason)
????})
??}
??static?all?(promises)?{
????return?new?Promise((resolve,?reject)?=>?{
??????let?resolvedResult?=?[]
??????let?resolvedCounter?=?0
??????for?(let?i?=?0;?i?????????promises[i].then((val)?=>?{
??????????resolvedCounter++
??????????resolvedResult[i]?=?val
??????????if?(resolvedCounter?===?promises.length)?{
????????????return?resolve(resolvedResult)
??????????}
????????},?e?=>?reject(e))
??????}
????})
??}
??static?race?(promises)?{
????return?new?Promise((resolve,?reject)?=>?{
??????for?(let?i?=?0;?i?????????//?只要有一個(gè)成功就成功,或者只要一個(gè)失敗就失敗
????????promises[i].then(resolve,?reject)
??????}
????})
??}
??then?(onFullFilled,?onRejected)?{
????//?可選參數(shù)需要為函數(shù),如果不傳或不是函數(shù)?則給出默認(rèn)參數(shù)
????onFullFilled?=?typeof?onFullFilled?===?'function'???onFullFilled?:?value?=>?value
????onRejected?=?typeof?onRejected?===?'function'???onRejected?:?reason?=>?{?throw?reason?}
????//?then?方法調(diào)用要返回?promise,即支持鏈?zhǔn)秸{(diào)用
????let?promise2?=?new?Promise((resolve,?reject)?=>?{
??????if?(this.status?===?'FULLFILLD')?{
????????//?當(dāng)前的onFulfilled,?onRejected不能在這個(gè)上下文執(zhí)行,要確保promise2存在,所以使用setTimeout
????????setTimeout(()?=>?{
??????????try?{
????????????let?x?=?onFullFilled(this.value)
????????????//?當(dāng)前的onFulfilled,?onRejected不能在這個(gè)上下文執(zhí)行,要確保promise2存在,所以使用setTimeout
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
??????if?(this.status?===?'REJECTED')?{
????????setTimeout(()?=>?{
??????????try?{
????????????let?x?=?onRejected(this.reason)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
??????//?pending?狀態(tài)就將函數(shù)收集到數(shù)組中去
??????if?(this.status?===?'PENDING')?{
????????this.onResolvedCallbacks.push(()?=>?{
??????????setTimeout(()?=>?{
????????????try?{
??????????????let?x?=?onFullFilled(this.value)
??????????????resolvePromise(promise2,?x,?resolve,?reject)
????????????}?catch?(e)?{
??????????????reject(e)
????????????}
??????????})
????????})
????????this.onRejectedCallbacks.push(()?=>?{
??????????setTimeout(()?=>?{
????????????try?{
??????????????let?x?=?onRejected(this.reason)
??????????????resolvePromise(promise2,?x,?resolve,?reject)
????????????}?catch?(e)?{
??????????????reject(e)
????????????}
??????????})
????????})
??????}
????})
????return?promise2
??}
}
//?在Promise的靜態(tài)方法上加如下方法可以通過(guò)一個(gè)npm模塊測(cè)試是否符合A+規(guī)范
//?https://github.com/promises-aplus/promises-tests?首先全局安裝promises-aplus-tests
//?->?npm?i?promises-aplus-tests?-g?再進(jìn)行測(cè)試?promises-aplus-tests?myPromise.js
Promise.deferred?=?function?()?{
??let?dfd?=?{}
??dfd.promise?=?new?Promise((resolve,?reject)?=>?{
????dfd.resolve?=?resolve
????dfd.reject?=?reject
??})
??return?dfd
}
//?promise?返回值處理函數(shù)
//?處理成功回調(diào)和失敗回調(diào)返回的?x?的類型
//?返回類型有3種情況?1、普通值?2、普通對(duì)象?3、promise對(duì)象
function?resolvePromise?(promise2,?x,?resolve,?reject)?{
??//?返回值不能是promise2,自己等待自己完成
??//?比如?p?=?new?Promise((resolve)=>?resolve()).then(()=>p);
??if?(promise2?===?x)?{
????return?new?TypeError('返回自身會(huì)導(dǎo)致死循環(huán)')
??}
??if?((typeof?x?===?'object'?&&?x?!=?null)?||?typeof?x?===?'function')?{
????let?called?//?控制?resolve?和?reject?只執(zhí)行一次,多次調(diào)用沒(méi)有任何作用
????try?{
??????let?then?=?x.then
??????if?(typeof?then?===?'function')?{?//?x?返回的是一個(gè)?promise?對(duì)象
????????/**
??????????*?這里不用x.then的原因?是x.then會(huì)取then方法的get
??????????*?如?Object.defineProperty(x,'then',{
??????????*?????get?()?{
??????????*????????throw?new?Error()
??????????*????}
??????????*?})
??????????*
??????????*??*/
????????then.call(x,
??????????y?=>?{?//?此處?y?還有可能返回一個(gè)?promise?所以用遞歸直到返回值為一個(gè)普通值為止
????????????if?(called)?return
????????????called?=?true?//?沒(méi)有調(diào)用過(guò)則?called?賦值為?true?來(lái)終止下次的調(diào)用
????????????resolvePromise(promise2,?y,?resolve,?reject)
??????????},
??????????r?=>?{
????????????if?(called)?return
????????????called?=?true
????????????reject(r)?//?直接用?reject?處理錯(cuò)誤就會(huì)直接斷掉?promise?的執(zhí)行
??????????}
????????)
??????}?else?{
????????resolve(x)?//?x?可能是一個(gè)普通的對(duì)象而非promise對(duì)象直接resolve
??????}
????}?catch?(e)?{
??????if?(called)?return?//?防止多次調(diào)用
??????called?=?true
??????reject(e)
????}
??}?else?{
????resolve(x)?//?x?可能是一個(gè)普通的值直接resolve
??}
}
/**
?*?創(chuàng)建promise
?*?@param?{Number}?value
?*/
function?makePromise?(value)?{
??return?new?Promise((resolve)?=>?{
????setTimeout(()?=>?{
??????resolve(value)
????},?Math.random()?*?1000)
??})
}
/**
?*?打印結(jié)果
?*?@param?{Number}?value
?*/
function?print?(value)?{
??console.log(value)
??return?value
}
let?promises?=?[1,?3,?4,?5,?6].map((item,?index)?=>?{
??return?makePromise(item)
})
//?并行執(zhí)行
Promise
??.all(promises)
??.then(()?=>?{
????console.log('done')
??})
??.catch(()?=>?{
????console.log('error')
??})
//?串行執(zhí)行
let?parallelPromises?=?promises.reduce((total,?currentValue)?=>?total.then(()?=>?currentValue.then(print)),?Promise.resolve())
parallelPromises.then(()?=>?{
??console.log('done')
}).catch(()?=>?{
??console.log('done')
})
compose
compose的參數(shù)是函數(shù)數(shù)組,返回的也是一個(gè)函數(shù)compose的參數(shù)是任意長(zhǎng)度的,所有的參數(shù)都是函數(shù),執(zhí)行方向是自右向左的,因此初始函數(shù)一定放到參數(shù)的最右面compose執(zhí)行后返回的函數(shù)可以接收參數(shù),這個(gè)參數(shù)將作為初始函數(shù)的參數(shù),所以初始函數(shù)的參數(shù)是多元的,初始函數(shù)的返回結(jié)果將作為下一個(gè)函數(shù)的參數(shù),以此類推。因此除了初始函數(shù)之外,其他函數(shù)的接收值是一元的。compose 和 pipe 的區(qū)別在于調(diào)用順序的不同
let?funcs?=?[fn1,?fn2,?fn3,?fn4]
let?composeFunc?=?compose(...funcs)
let?pipeFunc?=?pipe(...funcs)
//?compose
fn1(fn2(fn3(fn4(args))))
//?pipe
fn4(fn3(fn2(fn1(args))))
function?reduceFunc?(prev,?curr)?{
??return?(...args)?=>?{
????curr.call(this,?prev.apply(this,?args))
??}
}
function?compose?(...args)?{
??return?args.reverse().reduce(reduceFunc,?args.shift())
}
eventEmitter
const?wrapCb?=?(fn,?once?=?false)?=>?({?callback:?fn,?once?})
class?EventEmitter?{
??constructor?()?{
????this.events?=?new?Map()
??}
??//?綁定事件
??on?(event,?listener,?once?=?false)?{
????let?handler?=?this.events.get(event)
????if?(!handler)?{
??????this.events.set(event,?wrapCb(listener,?once))
????}?else?if?(handler?&&?typeof?handler.callback?===?'function')?{
??????//?如果只綁定一個(gè)回調(diào)
??????this.events.set(event,?[handler,?wrapCb(listener,?once)])
????}?else?{
??????//?綁定了多個(gè)回調(diào)
??????this.events.set(event,?[...handler,?wrapCb(listener,?once)])
????}
??}
??//?解綁事件
??off?(event,?listener)?{
????let?handler?=?this.events.get(event)
????if?(!handler)?return
????if?(!Array.isArray(handler))?{
??????//?簡(jiǎn)單比較回調(diào)函數(shù)是否相同
??????if?(String(handler.callback)?===?String(listener))?{
????????this.events.delete(event)
??????}?else?{
??????}
????}?else?{
??????//?循環(huán)函數(shù)回調(diào)隊(duì)列
??????for?(let?i?=?0;?i?????????const?item?=?handler[i]
????????if?(String(item.callback)?===?String(listener))?{
??????????handler.splice(i,?1)
??????????i--
????????}
??????}
????}
??}
??//?注冊(cè)一個(gè)單次監(jiān)聽(tīng)器
??once?(event,?listener)?{
????this.on(event,?listener,?true)
??}
??//?觸發(fā)事件,按監(jiān)聽(tīng)器的順序執(zhí)行執(zhí)行每個(gè)監(jiān)聽(tīng)器
??emit?(event,?...args)?{
????let?handler?=?this.events.get(event)
????if?(Array.isArray(handler))?{
??????//?拷貝到一個(gè)數(shù)組中,防止后續(xù)數(shù)組長(zhǎng)度出現(xiàn)變化,對(duì)數(shù)組的訪問(wèn)出錯(cuò)
??????let?eventsArr?=?[]
??????for?(let?i?=?0;?i?????????eventsArr.push(handler[i])
??????}
??????//?遍歷隊(duì)列,觸發(fā)每一個(gè)回調(diào)隊(duì)列
??????for?(let?i?=?0;?i?????????const?item?=?eventsArr[i]
????????item.callback.apply(this,?args)
????????if?(item.once)?{
??????????//?如果回調(diào)函數(shù)只運(yùn)行一次,則刪除該回調(diào)函數(shù)
??????????this.off(event,?item.callback)
????????}
??????}
????}?else?{
??????//?否則直接執(zhí)行即可
??????handler.callback.apply(this,?args)
????}
????return?true
??}
}
const?myEvent?=?new?EventEmitter()
const?listener1?=?(name)?=>?{
??if?(name)?{
????console.log(`監(jiān)聽(tīng)器?${name}?執(zhí)行。`)
??}?else?{
????console.log('監(jiān)聽(tīng)器 listener1 執(zhí)行。')
??}
}
const?listener2?=?()?=>?{
??console.log('監(jiān)聽(tīng)器 listener2 執(zhí)行。')
}
const?listener3?=?()?=>?{
??console.log('監(jiān)聽(tīng)器 listener3 執(zhí)行。')
}
myEvent.on('load',?listener1)
myEvent.on('load',?listener2)
myEvent.once('load',?listener3)
myEvent.emit('load')
myEvent.off('load',?listener2)
myEvent.emit('load',?'custom')
//?執(zhí)行結(jié)果如下:
//?監(jiān)聽(tīng)器 listener1 執(zhí)行。
//?監(jiān)聽(tīng)器 listener2 執(zhí)行。
//?監(jiān)聽(tīng)器 listener3 執(zhí)行。
//?監(jiān)聽(tīng)器 custom 執(zhí)行。
offset

getBoundingClientRect通過(guò) DOM React 來(lái)描述一個(gè)元素的具體位置。
const?offset?=?ele?=>?{
????let?result?=?{
????????top:?0,
????????left:?0
????}
????//?當(dāng)前為?IE11?以下,直接返回?{top:?0,?left:?0}
????if?(!ele.getClientRects().length)?{
????????return?result
????}
????//?當(dāng)前?DOM?節(jié)點(diǎn)的?display?===?'none'?時(shí),直接返回?{top:?0,?left:?0}
????if?(window.getComputedStyle(ele)['display']?===?'none')?{
????????return?result
????}
????result?=?ele.getBoundingClientRect()
????// ownerDocument 返回當(dāng)前節(jié)點(diǎn)的頂層的 document 對(duì)象。
????//?ownerDocument?是文檔,documentElement?是根節(jié)點(diǎn)
????var?docElement?=?ele.ownerDocument.documentElement?
????return?{
????????top:?result.top?+?window.pageYOffset?-?docElement.clientTop,
????????left:?result.left?+?window.pageXOffset?-?docElement.clientLeft
????}
}
Scheduler
class?Scheduler?{
??constructor?(num)?{
????this.num?=?num?//?允許同時(shí)運(yùn)行的異步函數(shù)的最大個(gè)數(shù)
????this.list?=?[]?//?用來(lái)承載還未執(zhí)行的異步
????this.count?=?0?//?用來(lái)計(jì)數(shù)
??}
??async?add?(fn)?{
????if?(this.count?>=?this.num)?{
??????//?通過(guò)?await?阻塞?Promise?但是又不執(zhí)行?resolve?,
??????//?而是將?resolve?保存到數(shù)組當(dāng)中去,
??????//?這樣就達(dá)到了當(dāng)異步任務(wù)超過(guò)?max?個(gè)時(shí)線程就會(huì)阻塞在第一行.
??????await?new?Promise((resolve)?=>?{?this.list.push(resolve)?})
????}
????this.count++
????const?result?=?await?fn()
????this.count--
????if?(this.list.length?>?0)?{
??????//?每執(zhí)行完一個(gè)異步任務(wù)就會(huì)去數(shù)組中查看一下有沒(méi)有還處于阻塞當(dāng)中的異步任務(wù),
??????//?如果有的話就執(zhí)行最前面的那個(gè)異步任務(wù).
??????this.list.shift()()
????}
????return?result
??}
}
const?schedule?=?new?Scheduler(2)//?最多同一時(shí)間讓它執(zhí)行3個(gè)異步函數(shù)
const?timeout?=?(time)?=>?new?Promise(resolve?=>?setTimeout(resolve,?time))
const?addTask?=?(time,?order)?=>?{
??schedule.add(()?=>?timeout(time)).then(()?=>?console.log(order))
}
addTask(1000,?1)
addTask(500,?2)
addTask(300,?3)
addTask(400,?4)
console.dir(schedule,?3)
//?output:?2,3,1,4
//?一開(kāi)始1、2?兩個(gè)任務(wù)進(jìn)入隊(duì)列
//?500ms?時(shí),2完成,輸出2,任務(wù)3進(jìn)隊(duì)
//?800ms?時(shí),3完成,輸出3,任務(wù)4進(jìn)隊(duì)
//?1000ms?時(shí),?1完成
//?1200ms?時(shí),4完成
useFetch
function?useFetch(fetch,?params,?visible?=?true)?{
??const?[data,?setData]?=?useState({});
??const?[loading,?setLoading]?=?useState(false);?//?fetch?數(shù)據(jù)時(shí)頁(yè)面?loading
??const?[newParams,?setNewParams]?=?useState(params);
??const?fetchApi?=?useCallback(async?()?=>?{
????console.log('useCallback');
????if?(visible)?{
??????setLoading(true);
??????const?res?=?await?fetch(newParams);
??????if?(res.code?===?1)?{
????????setData(res.data);
??????}
??????setLoading(false);
????}
??},?[fetch,?newParams,?visible]);
??useEffect(()?=>?{
????console.log('useEffect');
????fetchApi();
??},?[fetchApi]);
??const?doFetch?=?useCallback(rest?=>?{
????setNewParams(rest);
??},?[]);
??const?reFetch?=?()?=>?{
????setNewParams(Object.assign({},?newParams));?//?用戶點(diǎn)擊modal后加載數(shù)據(jù),或者當(dāng)請(qǐng)求參數(shù)依賴某個(gè)數(shù)據(jù)的有無(wú)才會(huì)fetch數(shù)據(jù)
??};
??return?{?loading,?data,?doFetch,?reFetch?};
}
useReducer
function?useReducer(reducer,?initialState)?{
??const?[state,?setState]?=?useState(initialState);
??function?dispatch(action)?{
????const?nextState?=?reducer(state,?action);
????setState(nextState);
??}
??return?[state,?dispatch];
}
combineReducers
/**
?*?Description:?組合不同的?Reducer
?*?
?*?@param?reducers
?*?@return?{Function}?finalReducer
?*/
rootReducer?=?combineReducers({potato:?potatoReducer,?tomato:?tomatoReducer})
//?rootReducer?將返回如下的?state?對(duì)象
{
??potato:?{
????//?...?potatoes,?和一些其他由?potatoReducer?管理的?state?對(duì)象?...
??},
??tomato:?{
????//?...?tomatoes,?和一些其他由?tomatoReducer?管理的?state?對(duì)象,比如說(shuō)?sauce?屬性?...
??}
}
function?combineReducers(reducers)?{
??//?返回合并后的新的?reducer?函數(shù)
??return?function?combination(state?=?{},?action)?{
????const?newState?=?{}
????Object.keys(reducers).map((key,?i)?=>?{
??????const?reducer?=?reducers[key]
??????//?執(zhí)行分?reducer,獲得?newState
??????newState[key]?=?reducer(state[key],?action)?//?這里通過(guò)?state[key]?來(lái)獲取分模塊的state,因此可知reducer的模塊名字要和state中保持一致
????})
????return?newState
??}
}
ErrorBoundary
class?ErrorBoundary?extends?React.Component?{
??constructor(props)?{
????super(props);
????this.state?=?{?hasError:?false?};
??}
?
??componentDidCatch(error,?info)?{
????//?Display?fallback?UI
????this.setState({?hasError:?true?});
????//?You?can?also?log?the?error?to?an?error?reporting?service
????logErrorToMyService(error,?info);
??}
?
??render()?{
????if?(this.state.hasError)?{
??????//?You?can?render?any?custom?fallback?UI
??????return?<h1>Something?went?wrong.h1>;
????}
????return?this.props.children;
??}
}
雙向數(shù)據(jù)綁定
HOC
const?Input?=?props?=>?(
??<>
????{props.value}
????
??>
)
const?HocInput?=?HocBind(Input)
const?App?=?()?=>?(
?????{?console.log("HocInput",?val)?}}?/>
)
const?HocBind?=?WrapperComponent?=>?{
??return?class?extends?React.Component?{
????state?=?{
??????value:?this.props.initialValue
????}
????onChange?=?e?=>?{
??????const?{?onChange?}?=?this.props;
??????this.setState({?value:?e.target.value?});
??????if?(onChange)?{
????????onChange(e.target.value);
??????}
????}
????render()?{
??????const?newProps?=?{
????????value:?this.state.props,
????????onChange:?this.onChange
??????};
??????return?<WrapperComponent?{...newProps}?/>;
????}
??};
};
Render Props
const?App?=?()?=>?(
??<HocInput?initialValue="init"?onChange={val?=>?{?console.log('HocInput',?val)?}}>
????{props?=>?(
??????<div>
????????<p>{props.value}p>
????????<input?placeholder="input"?{...props}?/>
??????div>
????)}
??HocInput>
);
class?HocInput?extends?React.Component?{
??state={
????value:?this.props.initialValue
??}
??onChange?=?(e)?=>?{
????const?{?onChange?}?=?this.props
????this.setState({?value:?e.target.value?})
????if?(onChange)?{
??????onChange(this.state.value)
????}
??}
??render()?{
????const?newProps?=?{
??????value:?this.state.value,
??????onChange:?this.onChange
????};
????return?(<div>
??????{this.props.children({...newProps})}
????div>)
??}
}
Hook
function?useBind(initialValue)?{
??const?[value,?setValue]?=?useState(initialValue)
??const?onChange?=?e?=>?{
????setValue(e.target.val)
??}
??return?{?value,?onChange?}
}
function?InputBind()?{
??const?{?value,?onChange?}?=?useBind('init')
??return?(
????<div>
??????<p>{value}p>
??????<input?onChange={onChange}/>
????div>
??)
}
總結(jié):我是一個(gè)不太愿意造這種小輪子的人,所以面試的時(shí)候是比較排斥的,我覺(jué)得能說(shuō)出基本的思路即可。自己完整整理了一遍,發(fā)現(xiàn)細(xì)節(jié)才是體現(xiàn)水平的地方,是對(duì)底層的深入理解。這是一個(gè)刻意練習(xí)的過(guò)程。
評(píng)論
圖片
表情
