在面試中,經常會遇到一些手寫XXX之類的面試題,所以好好總結一下,對於鞏固咱們的原生js的基礎是很是必要的。面試
儘管在網上已經有了很是多的總結文章,但在我看來有一個廣泛的問題,那就是把原理性的東西過於複雜化了。若是站在面試官的角度,他的目的是在最短的時間內考察出面試者對於JS語言的理解程度,可是在看了網站的諸多總結文章後我發現其中的代碼有很大一部分是作意義不大的操做,好比實現一個簡單的防抖,只要是核心的流程展現便可,至於其餘的一些等模式則沒有必要再去深挖,一大堆的if-else讓人看上去也眼花繚亂,甚至誤導別人直接去背代碼,另外,核心的邏輯都能展現出來,再去橫向的實現其餘的相似狀況恐怕也不是什麼問題了。segmentfault
在如下的整理中,建議你們先照的核心要點本身寫一遍,而後對照下面的代碼,複習效果更好。本文的目的就在於以最簡潔的代碼幫你從第一性原理的角度理解api的內部運做流程,凡是對於咱們理解api沒有幫助的的邊界狀況都不作處理。api
核心要點:數組
1.回調函數的參數有哪些,返回值如何處理。markdown
2.不修改原來的數組。閉包
Array.prototype.MyMap = function(fn, context){ var arr = Array.prototype.slice.call(this);//因爲是ES5因此就不用...展開符了 var mappedArr = []; for (var i = 0; i < arr.length; i++ ){ mappedArr.push(fn.call(context, arr[i], i, this)); } return mappedArr; } 複製代碼
核心要點:app
一、初始值不傳怎麼處理函數
二、回調函數的參數有哪些,返回值如何處理。oop
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 < arr.length; i++) { res = fn.call(null, res, arr[i], i, this); } return res; } 複製代碼
思路: 利用this的上下文特性。post
//實現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);//重點代碼,利用this指向,至關於context.caller(...args) delete context[fn]; return res; } 複製代碼
function create(proto) { function F() {}; F.prototype = proto; F.prototype.constructor = F; return new F(); } 複製代碼
核心要點:
1.對於普通函數,綁定this指向
2.對於構造函數,要保證原函數的原型對象上的屬性不能丟失
Function.prototype.bind = function(context, ...args) { let self = this;//謹記this表示調用bind的函數 let fBound = function() { //this instanceof fBound爲true表示構造函數的狀況。如new func.bind(obj) return self.apply(this instanceof fBound ? this : context || window, args.concat(Array.prototype.slice.call(arguments))); } fBound.prototype = Object.create(this.prototype);//保證原函數的原型對象上的屬性不丟失 return fBound; } 複製代碼
你們平時說的手寫bind,其實就這麼簡單:)
核心要點:
function myNew(fn, ...args) { let instance = Object.create(fn.prototype); let res = fn.apply(instance, args); return typeof res === 'object' ? res: instance; } 複製代碼
核心要點:原型鏈的向上查找。
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); } } 複製代碼
核心要點: 用閉包和Proxy屬性攔截
function proxy(func) { let instance; let handler = { construct(target, args) { if(!instance) { instance = Reflect.construct(func, args); } return instance; } } return new Proxy(func, handler); } 複製代碼
方式其實不少,以前我作過系統整理,有六種方法,請參考:
核心要點:
若是在定時器的時間範圍內再次觸發,則從新計時。
const debounce = (fn, delay) => { let timer = null; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }; 複製代碼
核心要點:
若是在定時器的時間範圍內再次觸發,則不予理睬,等當前定時器完成,才能啓動下一個定時器。
const throttle = (fn, delay = 500) => { let flag = true; return (...args) => { if (!flag) return; flag = false; setTimeout(() => { fn.apply(this, args); flag = true; }, delay); }; }; 複製代碼
參考個人另外一篇文章:
基於"發佈-訂閱"的原生JS插件封裝中的手寫發佈訂閱部分。
如下爲簡易版深拷貝,沒有考慮循環引用的狀況和Buffer、Promise、Set、Map的處理,若是一一實現,過於複雜,面試短期寫出來不太現實,若是有興趣能夠去這裏深刻實現:
const clone = parent => { // 判斷類型 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")) { // 對數組作特殊處理 child = []; } else if (isType(parent, "RegExp")) { // 對正則對象作特殊處理 child = new RegExp(parent.source, getRegExp(parent)); if (parent.lastIndex) child.lastIndex = parent.lastIndex; } else if (isType(parent, "Date")) { // 對Date對象作特殊處理 child = new Date(parent.getTime()); } else { // 處理對象原型 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); }; 複製代碼
重點難點,比較複雜,請參考個人另外一篇步步拆解文章:
也是重點知識,我以前作過詳細拆解,有五個版本,若是每一版本都能說清楚,可以很好的體現本身對於原型鏈的理解,文章地址: