Stories

Detail Return Return

模擬實現Javascript中的bind函數 - Stories Detail

bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定為 bind() 的第一個參數,而其餘參數將作為新函數的參數,供調用時使用。

從MDN對於bind的描述來看:

  • 返回值是一個函數,而不是執行結果
  • this值會指向第一個參數
  • 其餘參數會作為新函數的參數

看個例子:

function test(name, age) {
    console.log(this.name);
    this.name = name;
    this.age = age;
    console.log(this.name, this.age);
}

var obj = {
    name: 'Willem'
};

var bindFn = test.bind(obj, 'Wei');
bindFn(18);
// Willem
// Wei, 18

從上面的代碼就可以看出來,bind函數執行之後,bindFn的this值指向了obj,並且在bind的時候傳入的參數和在執行bindFn時傳入的參數都成功的傳入了test函數。

那代碼就呼之欲出了啊。

Function.prototype.wbind = function() {
    var context = [].shift.call(arguments);
    var args = [].slice.call(arguments);
    var self = this;

    return function() {
        var innerArgs = [].slice.call(arguments);
        
        self.apply(context, args.concat(innerArgs));
    }
}

相關:模擬實現Javascript中的call和apply

既然bind返回的是一個函數,那我有一個大膽的想法,如果我把這個返回的函數作為一個構造函數會怎樣呢?改造一下上面的那個例子:

function test(name, age) {
    console.log(this.name);
    this.name = name;
    this.age = age;
    console.log(this.name, this.age);
}

test.prototype.sayHi = function() {
    console.log('Hi, ' + this.name);
}

var obj = {
    name: 'Willem'
};

var bindFn = test.bind(obj, 'Wei');
var instance = new bindFn(18);
// undefined
// Wei,18
instance.sayHi(); // Hi, Wei
console.log(obj.name); // Willem

咦,obj對象裏面明明是有name屬性的啊,為啥第一次輸出的是undfined呢?明明傳入了name屬性為"Wei",為啥obj.name還是"Willem"呢?

其實是因為this並沒有指向obj了,而是指向了instance。總結一下,將返回的函數作為普通函數使用時,函數的this指向bind執行時傳入的第一個參數;將返回的函數作為構造函數使用時,函數的this指向實例,並且該實例將會繼承原函數原型上的屬性和方法。

這時候,我們再來改一改wbind函數

Function.prototype.wbind = function() {
    var context = [].shift.call(arguments);
    var args = [].slice.call(arguments);
    var self = this;

    var fBound = function() {
        var innerArgs = [].slice.call(arguments);
        // 做構造函數時,this指向實例
        self.apply(this instanceof fBound ? this : context, args.concat(innerArgs));
    }
    
    // 實例需要繼承原函數原型上的方法和屬性
    // 使用fNOP中轉一次是因為直接將this.prototype賦值到fNOP.prototype時
    // 當修改fNOP的prototype時,this.prototype也會被修改
    var fNOP = function() {}
    if (this.prototype) {
        fNOP.prototype = this.prototype;
    }
    
    // fBound.prototype = { __proto__: { this.prototype } }
    // 相當於是中間多了一個__proto__,因為原型鏈的緣故,所以多一層__proto__沒有什麼影響
    fBound.prototype = new fNOP(); 
    
    return fBound;
}

相關:
模擬實現js中的new操作符
簡單説説原型和原型鏈
使用ts模擬實現js中的一些函數和屬性

以上,就是bind的相關內容。

user avatar dingtongya Avatar linlinma Avatar yinzhixiaxue Avatar littlelyon Avatar linx Avatar yqyx36 Avatar zzd41 Avatar assassin Avatar nznznz Avatar yangxiansheng_5a1b9b93a3a44 Avatar congjunhua Avatar DolphinScheduler Avatar
Favorites 50 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.