前端面試必問:能否用js模擬實現call函數

前言

要想實現它,就必須先了解它是做什麼的,主要功能是什麼

MDN:Function.prototype.call() 文檔

一句話介紹call:call() 方法在使用一個指定的 this 值和若干個指定的參數值的前提下調用某個函數或方法。

舉個例子:

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1

從以上代碼發現:

1. call改變了this的指向,指向到了foo

2. bar函數執行了

模擬實現第一步

我們把bar方法作爲foo對象的屬性來調用,這樣this不就指向了foo嗎? 如下:

var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};

foo.bar(); // 1

但是這樣我們就改變了foo對象的屬性, 所以我們最後還是需要delete來刪除它。

所以我們的步驟可以分爲:

1. 將函數設爲對象的屬性

2. 執行該函數

3. 刪除該函數

根據這個思路我們就可以寫出第一版的call2函數:

// 第一版
Function.prototype.call2 = function(context) {
    // 首先要獲取調用call的函數,用this可以獲取
    context.fn = this;
    context.fn();
    delete context.fn;
}

測試一下

// 測試一下
var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call2(foo); // 1

模擬實現第二步

call函數還能給定參數執行函數,這裏需要注意一下,給定的參數是不確定的,我們需要怎麼取呢?

我們可以從Arguments對象中取值,取出第二個到最後一個參數,類似這樣

var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
}

// 此時args是類似這樣的["arguments[ 1]", "arguments[ 2]"]

但是我們需要怎麼將參數傳入執行的函數裏面呢???  

這裏我們使用eval方法,類似這樣

eval('context.fn(' + args +')')

eval方法:傳入一個字符串,會將該字符串解析爲js代碼來執行。

這樣我們就有了第二版的代碼

Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    eval('context.fn(' + args +')');
    delete context.fn;
}

// 測試一下
var foo = {
    value: 1
};

function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call2(foo, 'kevin', 18); 

模擬實現第三步 

寫到這裏,我們已經完成了80%,現在我們還有2個小細節沒有完成:

1. this 參數可以傳 null,當爲 null 的時候,視爲指向 window

2. 函數是可以有返回值的

Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;
}

這樣我們就完成了call函數的模擬實現,相信你看到這裏,已經可以自己實現了call函數,如果有什麼不懂的可以私信我或者評論區留言,學無止境,大家加油!!

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章