理解call和實現call

suydCao發表於2018-11-19

js裡對call的定義:

呼叫一個物件的一個方法,以另一個物件替換當前物件。call方法接受以下引數(obj,argument1,argument2,...)。

我們用call寫一個小demo:

理解call和實現call

如上圖我們就可以很直觀的理解js對call的定義。呼叫window物件下的fn方法,用obj物件替換了window物件。可能這句話理解還是有些歧義,我們之後再解釋。

為什麼說obj物件替換了window物件?見圖:

理解call和實現call

我們從程式碼裡看到直接呼叫fn輸出window下的name。因為fnwindow物件下,kack也在window下。 當我們用call的時候,把window物件替換成了obj,所以輸出了obj的name:'tom'

對於call的實現分析:

  1. call改變了this指向;
  2. 呼叫的方法執行了;
  3. call接受多個引數;

問題一:我們如何改變一個方法的this的指向呢? 問題二:如何去改變this指向,有去呼叫這個方法? 問題三:如何接受多個引數?再去執行?

理解call和實現call

可以看到我們給一個物件新增一個fn1屬性,並把fn方法賦給這個屬性,就相當於改變了fn裡的this指向!然後再呼叫obj.fn1這個屬性,不就執行了fn方法嗎?是不是很簡單~ 那多出來的fn1屬性怎麼辦?也很簡單,把這個物件的屬性刪除不就可以麼。。。那多個引數怎麼辦?我們獲取一個方法的引數可以通過arguments來獲取,arguments是一個偽陣列物件。然後遍歷這個物件把屬性再放到一個陣列裡,這樣就可以取出來了。 最後一個問題:得到引數陣列後怎麼放到方法裡去執行呢?我們要用到eval()函式,eval()函式可計算某個字串,並執行其中的的 JavaScript 程式碼。

我們先按上面的分析實現call:

Function.prototype._call = function(){
    var args = [];
    var obj = arguments[0] || window; // call 如果第一個引數傳null,則這個物件是window
    for(var i=1, len = arguments.length; i < len; i++){
        // 因為執行一個函式傳參一般都是傳string型別,所以此處需要用字串拼接,之後用eval去解析這個字串。
        args.push('arguments['+ i +']''); 
    }
    obj.fn = this; // this就是呼叫_call的函式
    // args = ['arguments[1]', 'arguments[2]', 'arguments[3]', ...];
    // 此處執行eval時,會先呼叫args.toString(),然後執行eval時會從對應的arguments物件中取引數。
    eval('obj.fn(' + args +')');
    delete obj.fn;
};
複製程式碼

執行以上程式碼:

理解call和實現call

大功告成,可以看出我們實現了call方法!

相關文章