這些手寫代碼會了嗎?少年
觀感度::star2::star2::star2::star2::star2:
口味:蟹黃豆腐
烹飪時間:5min
本文已收錄在前端食堂同名倉庫 Github
github.com/Geekhyt ,歡迎光臨食堂,如果覺得酒菜還算可口,賞個 Star 對食堂老闆來說是莫大的鼓勵。
從略帶銀絲的頭髮和乾淨利落的步伐我察覺到,面前坐着的這個面試官有點深不可測。我像往常一樣,準備花 3 分鐘的時間給面試官來一套昨天晚上精心準備的自我介紹。我自信且得意的訴說着對過往項目所付出的心血,所做的優化取得了怎樣的成果,爲公司提高了多少的收入。。。
顯然,面試官對我說的數字很感興趣,嘴角微微上揚,經過了一番細節的探討和技術的對線後。面試官拿出了一張紙。
手寫代碼。
注重基礎的面試官是靠譜的,爲了征服他,我一邊講解着實現原理一邊寫出了代碼。
手寫 call
call 和 apply 的區別:call 方法接收的是一個參數列表,apply 方法接收的是一個包含多個參數的數組。
- 1.
context
存在就使用context
,否則是window
- 2.使用
Object(context)
將context
轉換成對象,並通過context.fn
將this
指向context
- 3.循環參數,注意從
1
開始,第0
個是上下文,後面纔是我們需要的參數 - 4.將參數字符串
push
進args
- 5.字符串和數組拼接時,數組會調用
toString
方法,這樣可以實現將參數一個個傳入,並通過eval
執行 - 6.拿到結果返回前,刪除掉
fn
Function.prototype.call = function(context) { context = context ? Object(context) : window; context.fn = this; let args = []; for (let i = 1; i < arguments.length; i++) { args.push('arguments['+ i +']'); } let res = eval('context.fn('+ args +')'); delete context.fn; return res; }
手寫 apply
- 1.
apply
無需循環參數列表,傳入的args
就是數組 - 2.但是
args
是可選參數,如果不傳入的話,直接執行
Function.prototype.apply = function(context, args) { context = context ? Object(context) : window; context.fn = this; if (!args) { return context.fn(); } let res = eval('context.fn('+ args +')'); delete context.fn; return res; }
手寫 bind
- 1.
bind
的參數可以在綁定和調用的時候分兩次傳入 - 2.
bindArgs
是綁定時除了第一個參數以外傳入的參數,args
是調用時候傳入的參數,將二者拼接後一起傳入 - 3.如果使用
new
運算符構造綁定函數,則會改變this
指向,this
指向當前的實例 - 4.通過
Fn
鏈接原型,這樣fBound
就可以通過原型鏈訪問父類Fn
的屬性
Function.prototype.bind = function(context) { let that = this; let bindArgs = Array.prototype.slice.call(arguments, 1); function Fn () {}; function fBound(params) { let args = Array.prototype.slice.call(arguments) ; return that.apply(this instanceof fBound ? this : context, bindArgs.concat(args)); } Fn.prototype = this.prototype; fBound.prototype = new Fn(); return fBound; }
手寫 new
- 1.
Constructor
就是new
時傳入的第一個參數,剩餘的arguments
是其他的參數 - 2.使用
obj.__proto__ = Constructor.prototype
繼承原型上的方法 - 3.將剩餘的
arguments
傳給Contructor
,綁定this
指向爲obj
,並執行 - 4.如果構造函數返回的是引用類型,直接返回該引用類型,否則返回
obj
const myNew = function() { let Constructor = Array.prototype.shift.call(arguments); let obj = {}; obj.__proto__ = Constructor.prototype; let res = Constructor.apply(obj, arguments); return res instanceof Object ? res : obj; }
手寫 instanceOf
- 1.在
left
的原型鏈中層層查找,是否有原型等於prototype
- 2.確定邊界條件,如果
left === null
,即找到頭沒找到返回false
,right === left
,即找到返回true
- 3.
left = left.__proto__
,不停的向上查找
const myInstanceof = function(left, right) { right = right.prototype; left = left.__proto__; while (true) { if (left === null) { return false; } if (right === left) { return true; } left = left.__proto__; } }
手寫 Object.create
- 新建一個空的構造函數
F
,然後讓F.prototype
指向obj
,最後返回F
的實例
const myCreate = function (obj) { function F() {}; F.prototype = obj; return new F(); }