JavaScript溫故而知新——call()和apply()的實現

1、call()

call()經過指定this的指向來實現函數的間接調用,而且能夠傳入函數調用的參數
數組

舉個例子:bash

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

首先咱們要知道call()在這裏起的做用:
閉包

  1. 指定this的指向爲foo
  2. 執行bar函數

咱們能夠模擬一下這兩個效果:app

var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};
foo.bar();  // 1
複製代碼

能夠看到將bar設爲foo的屬性,便實現了將this指向foo。可是這樣作給foo對象無緣無故的添加了一個屬性,所以咱們還要經過delete刪除它。
所以主要實現思路以下:
函數

// 1.將函數設爲對象的屬性
foo.fn = bar
// 2.執行該函數
foo.fn()
// 3.從對象中刪除該函數
delete foo.fn
複製代碼

根據這個思路先實現一個簡易版的:post

Function.prototype.call2 = function(context) {
    // 首先要獲取調用call的函數,用this能夠獲取
    context.fn = this;
    context.fn();
    delete context.fn;
}
複製代碼

接下來實現call給定參數執行函數:
要注意傳入的參數個數是不肯定的,咱們能夠從arguments對象中取值,即第二個到最後一個參數即是要傳入的參數。測試

Function.prototype.call2 = function(context) {
    context.fn = this;
    
    var args = [];
    // 遍歷arguments類數組對象,取得第二個開始的參數,放到數組裏面去
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    // 執行後 args爲 [arguments[1], arguments[2], arguments[3]]
    
    // 這裏 args 會自動調用Array.toString()方法
    eval('context.fn(' + args +')');
    delete context.fn;
}
複製代碼

此時call()方法的核心已經實現了,下面要完善call()的功能:
ui

  1. 第一個參數能夠傳入null,表示指向window
  2. 函數是能夠有返回值的

完整實現this

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.call2(null); // 2
console.log(bar.call2(obj, 'kevin', 18));
// 1
// Object {
//    value: 1,
//    name: 'kevin',
//    age: 18
// }
複製代碼

2、apply()

apply的實現跟call相似,區別在於apply第二個參數傳入的是一個參數數組spa

Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}
複製代碼

結尾

系列文章:

相關文章
相關標籤/搜索