js基礎之call、apply、bind原理與實現

三者區別

  • apply、call、bind 三者都是函數的方法,第一個參數都是 this 要指向的對象,也就是想指定的上下文;html

  • apply、call、bind 三者均可以利用後續參數傳參,其中只有apply()後續參數以數組形式傳;segmentfault

  • bind 是返回對應的綁定函數,便於稍後調用;apply、call 則是當即調用數組

const parent = {
    age: 100,
    // 注意在用apply等時,這裏不能用箭頭函數,箭頭定義時this已經指定,沒法被apply等改變
    run: function(name) { console.log(this.age, name) }
};
const child = {
    age: 1
};

parent.run.apply(child, ["tom"]); // 1 tom // 當即執行
parent.run.call(child, "tom"); // 1 tom // 當即執行
parent.run.apply(null); // undefined undefined // null 是window下的,表示this指向window
parent.run.apply(undefined); // undefined undefined // undefined 指向window

const fn = parent.run.bind(child, "tom"); // bind會返回一個函數
fn(); // 1 tom
複製代碼

原理 & 實現

call & apply

更詳細能夠參考JavaScript深刻之call和apply的模擬實現app

fn.apply(obj, array)實現的原理:函數

  • 將要執行的函數fn設置爲該對象obj的屬性
  • 執行obj.fn(args)函數
  • 不能給這個obj對象新增新的屬性,因此要刪除這個屬性;
  • 其中對obj要作判斷,爲null或undefined時,要把this指向window
Function.prototype.call = function (context) {

    // 首先判斷是否是函數調用該方法,若是不是,則拋出異常
    if (typeof this !== "function") throw new TypeError('Error');

    // 爲**null或undefined**時,要把this指向window
    var context = context || window;

    // 將函數設置爲obj的屬性
    context.fn = this;

    // 類數組解構參數
    var args = [...arguments].slice(1);

    // 執行函數
    var result = context.fn(...args);

    // 刪除函數
    delete context.fn;

    // 返回執行結果
    return result;
}

// apply與call不一樣只在於參數處理不用
Function.prototype.apply = function (context, args) {

    // 首先判斷是否是函數調用該方法,若是不是,則拋出異常
    if (typeof this !== "function") throw new TypeError('Error');

    // 爲**null或undefined**時,要把this指向window
    var context = context || window;

    // 將函數設置爲obj的屬性
    context.fn = this;

    // 執行函數
    var result;
    if (args) {
        result = context.fn(...args);
    } else {
        result = context.fn();
    }

    // 刪除函數
    delete context.fn;

    // 返回執行結果
    return result;
}
複製代碼

bind

bind實際上是在call、apply基礎上封裝了一層,所以屢次調用bind,只會第一次生效;ui

parent.run.bind(child, "tom").bind(child1, "jack").bind(child2, "Rachael")();
// 1 tom 
// 並不會繼續下去
複製代碼

簡單的實現:(實際要複雜一些)this

Function.prototype.myBind = function (context) {

    // 首先判斷是否是函數調用該方法,若是不是,則拋出異常
    if (typeof this !== "function") throw new TypeError('Error');

    var me = this;

    // 去掉第一個函數參數
    var args = [...arguments].slice(1);
    // var args = Array.prototype.slice(arguments, 1);

    return function () {
        // 把後面的執行函數的參數拼接到bing參數後
        return me.apply(context, args.concat(...arguments);
        // return me.apply(context, args.concat(Array.prototype.slice(arguments));
    }
}
複製代碼

首發於本人的博客:柴犬工做室spa

相關文章
相關標籤/搜索