模擬實現Javascript中的bind函數

bind() 方法建立一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其他參數將做爲新函數的參數,供調用時使用。

從MDN對於bind的描述來看:javascript

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

看個例子:java

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函數。segmentfault

那代碼就呼之欲出了啊。app

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返回的是一個函數,那我有一個大膽的想法,若是我把這個返回的函數做爲一個構造函數會怎樣呢?改造一下上面的那個例子:this

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"呢?prototype

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

這時候,咱們再來改一改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操做符
簡單說說原型和原型鏈繼承

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

相關文章
相關標籤/搜索