在面試中,常常會(huì)遇到一些手寫(xiě)XXX之類(lèi)的面試題,因此好好總結(jié)一下,對(duì)于鞏固我們的原生js的基礎(chǔ)是非常必要的。
我們提供的服務(wù)有:網(wǎng)站建設(shè)、成都做網(wǎng)站、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、扎蘭屯ssl等。為千余家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢(xún)和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的扎蘭屯網(wǎng)站制作公司盡管在網(wǎng)上已經(jīng)有了非常多的總結(jié)文章,但在我看來(lái)有一個(gè)普遍的問(wèn)題,那就是把原理性的東西過(guò)于復(fù)雜化了。如果站在面試官的角度,他的目的是在最短的時(shí)間內(nèi)考察出面試者對(duì)于JS語(yǔ)言的理解程度,但是在看了網(wǎng)站的諸多總結(jié)文章后我發(fā)現(xiàn)其中的代碼有很大一部分是做意義不大的操作,比如實(shí)現(xiàn)一個(gè)簡(jiǎn)單的防抖,只要是核心的流程展示即可,至于其他的一些等模式則沒(méi)有必要再去深挖,一大堆的if-else讓人看上去也眼花繚亂,甚至誤導(dǎo)別人直接去背代碼,另外,核心的邏輯都能展示出來(lái),再去橫向的實(shí)現(xiàn)其他的類(lèi)似情況恐怕也不是什么問(wèn)題了。
在以下的整理中,建議大家先照的核心要點(diǎn)自己寫(xiě)一遍,然后對(duì)照下面的代碼,復(fù)習(xí)效果更好。本文的目的就在于以最簡(jiǎn)潔的代碼幫你從第一性原理的角度理解api的內(nèi)部運(yùn)作流程,凡是對(duì)于我們理解api沒(méi)有幫助的的邊界情況都不做處理。
一、用ES5實(shí)現(xiàn)數(shù)組的map方法
核心要點(diǎn):
1.回調(diào)函數(shù)的參數(shù)有哪些,返回值如何處理。
2.不修改原來(lái)的數(shù)組。
Array.prototype.MyMap?=?function(fn,?context){ ?var?arr?=?Array.prototype.slice.call(this);//由于是ES5所以就不用...展開(kāi)符了 ?var?mappedArr?=?[]; ?for?(var?i?=?0;?i?二、用ES5實(shí)現(xiàn)數(shù)組的reduce方法
核心要點(diǎn):
1、初始值不傳怎么處理
2、回調(diào)函數(shù)的參數(shù)有哪些,返回值如何處理。
Array.prototype.myReduce?=?function(fn,?initialValue)?{ ?var?arr?=?Array.prototype.slice.call(this); ?var?res,?startIndex; ?res?=?initialValue???initialValue?:?arr[0]; ?startIndex?=?initialValue???0?:?1; ?for(var?i?=?startIndex;?i?三、實(shí)現(xiàn)call/apply
思路: 利用this的上下文特性。
//實(shí)現(xiàn)apply只要把下一行中的...args換成args即可? Function.prototype.myCall?=?function(context?=?window,?...args)?{ ?let?func?=?this; ?let?fn?=?Symbol("fn"); ?context[fn]?=?func; ?let?res?=?context[fn](...args);//重點(diǎn)代碼,利用this指向,相當(dāng)于context.caller(...args) ?delete?context[fn]; ?return?res; } 復(fù)制代碼四、實(shí)現(xiàn)Object.create方法(常用)
function?create(proto)?{ ?function?F()?{}; ?F.prototype?=?proto; ?F.prototype.constructor?=?F; ? ?return?new?F(); } 復(fù)制代碼五、實(shí)現(xiàn)bind方法
核心要點(diǎn):
1.對(duì)于普通函數(shù),綁定this指向
2.對(duì)于構(gòu)造函數(shù),要保證原函數(shù)的原型對(duì)象上的屬性不能丟失
Function.prototype.bind?=?function(context,?...args)?{ ?let?self?=?this;//謹(jǐn)記this表示調(diào)用bind的函數(shù) ?let?fBound?=?function()?{ ?//this?instanceof?fBound為true表示構(gòu)造函數(shù)的情況。如new?func.bind(obj) ?return?self.apply(this?instanceof?fBound???this?:?context?||?window,?args); ?} ?fBound.prototype?=?Object.create(this.prototype);//保證原函數(shù)的原型對(duì)象上的屬性不丟失 ?return?fBound; } 復(fù)制代碼大家平時(shí)說(shuō)的手寫(xiě)bind,其實(shí)就這么簡(jiǎn)單:)
六、實(shí)現(xiàn)new關(guān)鍵字
核心要點(diǎn):
創(chuàng)建一個(gè)全新的對(duì)象,這個(gè)對(duì)象的__proto__要指向構(gòu)造函數(shù)的原型對(duì)象
執(zhí)行構(gòu)造函數(shù)
返回值為object類(lèi)型則作為new方法的返回值返回,否則返回上述全新對(duì)象
function?myNew(fn,?...args)?{ ?let?instance?=?Object.create(fn.prototype); ?let?res?=?fn.apply(instance,?args); ?return?typeof?res?===?'object'???res:?instance; } 復(fù)制代碼七、實(shí)現(xiàn)instanceof的作用
核心要點(diǎn):原型鏈的向上查找。
function?myInstanceof(left,?right)?{ ?let?proto?=?Object.getPrototypeOf(left); ?while(true)?{ ?if(proto?==?null)?return?false; ?if(proto?==?right.prototype)?return?true; ?proto?=?Object.getPrototypeof(proto); ?} } 復(fù)制代碼八、實(shí)現(xiàn)單例模式
核心要點(diǎn): 用閉包和Proxy屬性攔截
function?proxy(func)?{ ?let?instance; ?let?handler?=?{ ?constructor(target,?args)?{ ?if(!instance)?{ ?instance?=?Reflect.constructor(fun,?args); ?} ?return?instance; ?} ?} ?return?new?Proxy(func,?handler); } 復(fù)制代碼九、實(shí)現(xiàn)數(shù)組的flat
方式其實(shí)很多,之前我做過(guò)系統(tǒng)整理,有六種方法,請(qǐng)參考:
JS數(shù)組扁平化(flat)方法總結(jié)
十、實(shí)現(xiàn)防抖功能
核心要點(diǎn):
如果在定時(shí)器的時(shí)間范圍內(nèi)再次觸發(fā),則重新計(jì)時(shí)。
const?debounce?=?(fn,?delay)?=>?{ ?let?timer?=?null; ?return?(...args)?=>?{ ?clearTimeout(timer); ?timer?=?setTimeout(()?=>?{ ?fn.apply(this,?args); ?},?delay); ?}; }; 復(fù)制代碼十一、實(shí)現(xiàn)節(jié)流功能
核心要點(diǎn):
如果在定時(shí)器的時(shí)間范圍內(nèi)再次觸發(fā),則不予理睬,等當(dāng)前定時(shí)器完成,才能啟動(dòng)下一個(gè)定時(shí)器。
const?throttle?=?(fn,?delay?=?500)?=>?{ ?let?flag?=?true; ?return?(...args)?=>?{ ?if?(!flag)?return; ?flag?=?false; ?setTimeout(()?=>?{ ?fn.apply(this,?args); ?flag?=?true; ?},?delay); ?}; }; 復(fù)制代碼十二、用發(fā)布訂閱模式實(shí)現(xiàn)EventEmit
參考我的另一篇文章:
基于"發(fā)布-訂閱"的原生JS插件封裝中的手寫(xiě)發(fā)布訂閱部分。
十三、實(shí)現(xiàn)深拷貝
以下為簡(jiǎn)易版深拷貝,沒(méi)有考慮循環(huán)引用的情況和Buffer、Promise、Set、Map的處理,如果一一實(shí)現(xiàn),過(guò)于復(fù)雜,面試短時(shí)間寫(xiě)出來(lái)不太現(xiàn)實(shí),如果有興趣可以去這里深入實(shí)現(xiàn):
深拷貝終極探索。
const?clone?=?parent?=>?{ ?//?判斷類(lèi)型 ?const?isType?=?(target,?type)?=>?`[object?${type}]`?===?Object.prototype.toString.call(target) ?//?處理正則 ?const?getRegExp?=?re?=>?{ ?let?flags?=?""; ?if?(re.global)?flags?+=?"g"; ?if?(re.ignoreCase)?flags?+=?"i"; ?if?(re.multiline)?flags?+=?"m"; ?return?flags; ?}; ?const?_clone?=?parent?=>?{ ?if?(parent?===?null)?return?null; ?if?(typeof?parent?!==?"object")?return?parent; ?let?child,?proto; ?if?(isType(parent,?"Array"))?{ ?//?對(duì)數(shù)組做特殊處理 ?child?=?[]; ?}?else?if?(isType(parent,?"RegExp"))?{ ?//?對(duì)正則對(duì)象做特殊處理 ?child?=?new?RegExp(parent.source,?getRegExp(parent)); ?if?(parent.lastIndex)?child.lastIndex?=?parent.lastIndex; ?}?else?if?(isType(parent,?"Date"))?{ ?//?對(duì)Date對(duì)象做特殊處理 ?child?=?new?Date(parent.getTime()); ?}?else?{ ?//?處理對(duì)象原型 ?proto?=?Object.getPrototypeOf(parent); ?//?利用Object.create切斷原型鏈 ?child?=?Object.create(proto); ?} ?for?(let?i?in?parent)?{ ?//?遞歸 ?child[i]?=?_clone(parent[i]); ?} ?return?child; ?}; ?return?_clone(parent); }; 復(fù)制代碼十四、實(shí)現(xiàn)Promise
重點(diǎn)難點(diǎn),比較復(fù)雜,請(qǐng)參考我的另一篇步步拆解文章:
我如何實(shí)現(xiàn)Promise
十五、使用ES5實(shí)現(xiàn)類(lèi)的繼承效果
也是重點(diǎn)知識(shí),我之前做過(guò)詳細(xì)拆解,有五個(gè)版本,如果每一版本都能說(shuō)清楚,能夠很好的體現(xiàn)自己對(duì)于原型鏈的理解,文章地址:
ES5實(shí)現(xiàn)繼承的那些事
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。