觀感度::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.fnthis 指向 context
  • 3.循環參數,注意從 1 開始,第 0 個是上下文,後面纔是我們需要的參數
  • 4.將參數字符串 pushargs
  • 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 ,即找到頭沒找到返回 falseright === 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();
}

相關文章