Js:模擬實現call函數

call

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

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

需要注意兩點:

  • call改變了this的指向,指向到foo;
  • 調用了bar函數

模擬實現第一步

試想當調用 call 的時候,把 foo 對象改造成如下:

//我們希望把bar里的this指向foo;那我們把bar放進foo里面能實現這個效果
var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};
foo.bar(); // 1

這樣我們就實現了this指向foo;
但是我們給foo添加了一個屬性才實現了這個效果,那我們用完可以刪除掉這個屬性。
模擬步驟可分為:

// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn
//fn 是對象的屬性名,反正最后也要刪除它,所以起成什么都無所謂。

根據這個思路,我們可以嘗試著去寫第一版的 call2 函數:

Function.Prototype.call2 = function(context){
  //獲取調用call2的函數,用this
  context.fn = this;
  context.fn();
  delete context.fn;

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

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

bar.call2(foo); // 1

這樣我們就輕松模擬了call指定this指向的功能;

模擬實現第二步

call 函數還能給定參數執行函數。
舉個例子:

var foo = {
    value: 1
};

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

bar.call(foo, 'Cherry', 18);
// Cherry
// 18
// 1

注意:傳入的參數并不確定,這可咋辦?

不急,我們可以從 Arguments 對象中取值,取出第二個到最后一個參數,然后放到一個數組里。

比如這樣:

// 以上個例子為例,此時的arguments為:
// arguments = {
//      0: foo,
//      1: 'Cherry',
//      2: 18,
//      length: 3
// }
// 因為arguments是類數組對象,所以可以用for循環
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
}

// 執行后 args為 ["arguments[1]", "arguments[2]", "arguments[3]"]

不定長的參數問題解決了,我們接著要把這個參數數組放到要執行的函數的參數里面去。

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

所以我們的第二版克服了兩個大問題,代碼如下:

// 第二版
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, 'Cherry', 18); 
// Cherry
// 18
// 1

模擬實現第三步

模擬代碼已經完成 80%,還有兩個小點要注意:

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;
}

// 測試一下
var value = 2;

var obj = {
    value: 1
}

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

bar.call(null); // 2

console.log(bar.call2(obj, 'Cherry', 18));
// 1
// Object {
//    value: 1,
//    name: 'Cherry',
//    age: 18
// }

到此,我們完成了 call 的模擬實現。

文章非原創,有侵權請告知,文章出處:
https://github.com/mqyqingfeng/Blog/issues/11

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容