bind的模擬實現

apply

apply的方法和 call 方法的實現相似,只不過是若是有參數,以數組形式進行傳遞,直接上代碼:javascript

Function.prototype.apply2 = function(context) {
    var context = context || window
    context.fn = this // this 也就是調用apply的函數
    var result
    // 判斷是否有第二個參數
    if(arguments[1]) {
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    delete context.fn()
    return result
}

var foo = {
    value: 1
}
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}
bar.apply2(foo, ['black', '18']) // black 18 1

bind

bind() 方法會建立一個新的函數。當這個新函數被調用時,bind() 的第一個參數會做爲它運行時的this,以後的參數將會在傳遞的實參前傳入做爲它的參數。

也就是說bind能夠返回一個函數,而且能夠傳入參數。java

首先根據第一點,須要返回一個函數:數組

Function.prototype.bind2 = function(context) {
    var self = this
    return function() {
        self.apply(context)
    }
}
var foo = {
    value: 1
};

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

var bindFoo = bar.bind2(foo); 
bindFoo() // 1

接下來還有第二點,能夠傳入參數,但是傳參數還會有兩種狀況,是在bind的時候傳參數呢,仍是在調用返回的函數的時候傳參數呢?app

var foo = {
    value: 1
};

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

var bindFoo = bar.bind(foo, 'black');
bindFoo('18')
// black
// 18
// 1

經過上面的例子就能夠明顯發現。兩種方式均可以傳參。那模擬實現:函數

Function.prototype.bind2 = function(context) {
    var self = this
    var args = [...arguments].slice(1)
    console.log([...arguments])
    return function() {
        // 這個時候的arguments指調用的時候傳入的參數
        self.apply(context, args.concat([...arguments]))
    }
}

var foo = {
    value: 1
};

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

var bindFoo = bar.bind2(foo, 'black');
bindFoo('18')

這兩點完成後,還有一個特色,就是一個綁定函數也能使用new操做符建立對象,提供的this值被忽略,同時調用時的參數被提供給模擬函數。this

也就是說 bind 返回的函數做爲構造函數的時候,bind 時指定的 this 值會失效,可是傳入的參數依然生效,舉個例子來講明:prototype

var value = 2;
var foo = {
    value: 1
}
function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}

bar.prototype.sex = 'boy'
var bindFoo = bar.bind(foo, 'black')
var obj = new bindFoo('18')
// black
// 18
// undefined
console.log(obj.sex) // boy

儘管在全局和 foo 中都聲明瞭 value 值,最後依然返回 undefined,那是由於當調用obj的時候,這個時候的this指向已經指向到了 objcode

因此咱們要去修改一下返回函數的原型來去實現:對象

Function.prototype.bind2 = function(context) {
    var self = this;
    var args = [...arguments].slice(1)
    
    var fun = function() {
        // 看成爲構造函數的時候,this 指向實例, self指向綁定函數,由fun.prototype = this.prototype, 使 fun.prototype 爲 綁定函數的 prototype,此時結果爲true,因此this指向實例
        // 做爲普通函數時,this 指向window, self 指向綁定函數,此時結果爲false,此時this指向綁定的context
        self.apply(this instanceof self ? this : context,args.concat(...arguments))
    }
    // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承函數的原型中的值
    // 使用Object.create避免修改函數的prototype
    fun.prototype = Object.create(this.prototype)
    return fun
}

最後加個對調用bind是不是個函數的判斷:繼承

Function.prototype.bind2 = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Error')
    }
    var self = this;
    var args = [...arguments].slice(1)
    
    var fun = function() {
        // 看成爲構造函數的時候,this 指向實例, self指向綁定函數,由fun.prototype = this.prototype, 使 fun.prototype 爲 綁定函數的 prototype,此時結果爲true,因此this指向實例
        // 做爲普通函數時,this 指向window, self 指向綁定函數,此時結果爲false,此時this指向綁定的context
        self.apply(this instanceof self ? this : context,args.concat(...arguments))
    }
    // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承函數的原型中的值
    // 使用Object.create避免修改函數的prototype
    fun.prototype = Object.create(this.prototype)
    return fun
}
相關文章
相關標籤/搜索