1. 程式人生 > >原生實現call、apply方法

原生實現call、apply方法

大學的時候,一位大佬跟我說,讓我用原生寫一下call、apply方法,我沒寫。直到現在。。。。拖延症真的不是吹的。

call

先來看官方解釋:

call()方法呼叫一個函式,其具有一個指定的this值和分別地提供的引數(引數的列表)。

也就是說使用call方法,你可以指定被呼叫函式的this值,並且給他傳引數。

接下來我們自己實現一個call方法。

1、模擬如何指定this的值

先來看一段程式碼

function func() {
    console.log(this.value)
}
var obj = {
    value: 233
}
func.call(obj); //233

就是在呼叫func的時候,我們設定了當前this為obj,所以輸出的this.value 就是233。不多說了。

試想一下,call方法是要改變this的值,我們可以給obj物件加一個屬性fn,把func函式賦值給它,呼叫obj.fn,this自然就指向物件obj了,也起到了呼叫func函式的作用。所以我們可以這麼實現:
1. 給物件obj新增fn屬性,並賦值func函式
2、呼叫它–> obj.fn()
3、delete obj.fn

function func() {
    console.log(this.value)
}
var obj = {
    value: 233
}
Function.prototype.mycall = function(obj) {

    obj.fn = this; //這裡的this就是呼叫mycall的func函式
    obj.fn();
    delete.fn;
}
func.mycall(obj); //233
ok,現在我們已經實現瞭如何指定的this的值。

2、實現可傳參

Function.prototype.mycall = function(obj) {

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
       args.push('arguments[' + i + ']');
    }
    obj.fn = this;
    eval('obj.fn('+ args +')’);
    delete obj.fn;
}

多說一句,我們拿到的args陣列其實是[‘arguments[0]’, ‘arguments[1]’,…]這樣子的,如果我們直接去拿arguments[0]的值的話,你測試一下傳一個字串引數,像這樣,func.mycall(obj, ‘dd’);就會報錯,告訴你dd is not defined。 因為eval會把字串解析成一個變數。

這裡遇到了一個問題,如何給一個函式傳遞不定參? 或者說怎麼把不定長的陣列轉換成多個引數傳遞給函式??
三種辦法: eval、apply和es6 的解構語法

1、eval('obj.fn('+ args +')');
2、obj.fn.apply(obj, args); 
3、obj.fn(…args);//es6解構語法

3、完善小細節

到這裡,我們基本已經實現了call方法的功能,還有兩個點需要注意:
– 如果第一個引數為null,this指向window
– 函式呼叫是有返回值的

呈上終極程式碼。

Function.prototype.mycall = function(obj) {

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
       args.push('arguments[' + i + ']');
    }
    obj = obj || window;
    obj.fn = this;
    var result = eval('obj.fn('+ args +')’);
    delete obj.fn;
    return result;
}

apply

講完了call方法,我們來說一下apply方法,apply方法和call方法非常類似。同樣,先來看一下官方的解釋:
apply()方法呼叫一個函式,其具有一個指定的this值,以及作為一個數組(或類陣列物件)提供的引數。 注意:call()方法的作用和 apply() 方法類似,只有一個區別,就是 call()方法接受的是若干個引數的列表,而apply()方法接受的是一個包含多個引數的陣列。

好,我們知道apply和call方法的區別就在於,call是傳引數列表,apply是傳一個數組。話不多說,開始寫程式碼。

Function.prototype.myapply = function(obj, arr) {

    obj = obj || window;
    obj.func = this;
    var result;
    if(!arr) {
        result = obj.func();
    } else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('obj.func('+ args +')');
    }
    delete obj.func;
    return result;
}

本文參考掘金上面的一篇文章JavaScript深入之call和apply的模擬實現

寫這篇部落格呢,是為了整理一下call和apply的實現方法,也梳理一下自己的思路,能更好的理解和掌握。
如有錯誤,懇請指正。