觀感度:?????
口味:蟹黃豆腐
烹飪時間: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();
}