JavaScript之bind模擬實現

JavaScript之bind模擬實現javascript

一句話介紹bind:
bind() 方法會建立一個新函數,當這個新函數被調用時,bind()的第一個參數將做爲他運行時的this,以後的一序列參數將會在傳遞的實參前傳入做爲他的參數
 
由此能夠看出兩個特色:
  1. 返回一個函數
  2. 能夠傳入參數
 
返回函數的模擬實現
第一個特色,咱們舉個例子(返回一個函數)
var obj = {
    value : 1
}

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

var foo1 = foo.bind(obj,'hy')
foo1(18)// hy 18 1

模擬實現第一步java

Function.prototype.bind2 = function(context){
    var that = this
    return function(){
        return that.apply(context)
    }
}

之因此 returnthat.apply(context) ,是考慮到綁定函數可能有返回值。app

var obj = {
    value : 1
}
function foo(){
    return this.value
}

var foo1 = foo.bind(obj)
console.log(foo1()). // 1

傳參的模擬實現函數

咱們舉個例子(能夠傳參)
var obj = {
    value : 1
}
function foo(name,age){
    console.log(this.value)
    console.log(name)
    console.log(age)
}
var Foobind = foo.bind(obj,'hy')
Foobind(18)

函數須要傳name和age兩個參數,居然還能夠在bind的時候,只傳一個name,在執行返回的函數的時候,在傳另外一個參數age優化

模擬實現第二步
Function.prototype.bind2 = function(context){
    var that = this
    // 獲取bind2函數從第二個參數到最後一個參數
    var args = Array.prototype.slice.call(arguments,1)
    return function(){
        // 這個時候的 arguments 是指bind返回的函數傳入的參數
        var bindArgs = Array.prototype.slice.call(arguments)
        return that.apply(context,args.concat(bindArgs))
    }
}

構造函數效果的模擬實現this

最難的部分到了,由於bind還有一個特色,就是:
  1. 一個綁定函數也能使用new操做符建立對象:這種行爲就像把原函數當成構造器。提供的this值被忽略,同時調用時的參數被提供給模擬函數
也就是說當bind返回的函數做爲構造函數的時候,bind時指定的this值會失效,但傳入的值依然有效
var value = 2
    var obj = {
    value : 1
}

function foo(name,age){
    this.name = 'ycl'
    console.log(this.value)// undefined
    console.log(name)// hy
    console.log(age)// 18
}
foo.prototype.flag = '1-'

var bindFoo = foo.bind(obj,'hy')
var bindFoo1 = new bindFoo(18)

console.log(bindFoo1.name)// ycl
console.log(bindFoo1.flag)// 1-

注意 :儘管在全局和obj 都聲明瞭value值,但最後返回了undefined,說明綁定this失效了。spa

因此咱們能夠經過修改返回的函數的原型來實現
模擬實現第三步
Function.prototype.bind2=function(context){
    var that = this
    var args = Array.prototype.slice.call(arguments,1)
    var fBound = function(){
        var bindArgs = Array.prototype.slice.call(arguments)
        // 看成爲構造函數時,this 指向實例,此時結果爲true,將綁定函數的this指向該實例,能夠讓實例得到來自綁定函數的值
        // 看成爲普通函數時,this 指向window,此時結果爲false,將綁定函數的this指向context
        return that.apply(this instanceof fBound ? this : context , args.concat(bindArgs))
    }
    // 修改返回函數的 prototype 爲綁定函數的prototype,實例就能夠繼承綁定函數的原型的值 (此例子的綁定函數爲foo)
    fBound.prototype = this.prototype
    return fBound
}

構造函數效果的優化實現prototype

可是在這個寫法中,咱們直接將 fBound . prototype = this . prototype ,咱們直接修改 fBound . prototype的時候,也會直接修改綁定函數的prototype。這個時候咱們經過一個空函數進行中轉。
模擬實現第四步
Function.prototype.bind2 = function(context){
    var that = this
    var args = Array.prototype.slice.call(arguments , 1)
    var fBound = function(){
        var bindArgs = Array.prototype.slice.call(arguments)
        var fNOP = function(){}
        // 看成爲構造函數時,this 指向實例,此時結果爲true,將綁定函數的this指向該實例,能夠讓實例得到來自綁定函數的值
        // 看成爲普通函數時,this 指向window,此時結果爲false,將綁定函數的this指向context
        return that.apply(this instanceof fNOP ? this : context , args.concat(bindArgs))
    }
    // 修改返回函數的 prototype 爲綁定函數的prototype,實例就能夠繼承綁定函數的原型的值 (此例子的綁定函數爲foo)
    fNOP.prototype = this.prototype
    fBound.prototype = new fNOP()
    return fBound
}

若是調用bind的不是函數咋辦?code

if(typeof this !== 'function'){
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}

最後完成版本對象

Function.prototype.bind2 = function(context){
    if(typeof this !== 'function'){
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var that = this
    var args = Array.prototype.slice.call(arguments , 1)
    var fBound = function(){
        var bindArgs = Array.prototype.slice.call(arguments)
        var fNOP = function(){}
        // 看成爲構造函數時,this 指向實例,此時結果爲true,將綁定函數的this指向該實例,能夠讓實例得到來自綁定函數的值
        // 看成爲普通函數時,this 指向window,此時結果爲false,將綁定函數的this指向context
        return that.apply(this instanceof fNOP ? this : context , args.concat(bindArgs))
    }
    // 修改返回函數的 prototype 爲綁定函數的prototype,實例就能夠繼承綁定函數的原型的值 (此例子的綁定函數爲foo)
    fNOP.prototype = this.prototype
    fBound.prototype = new fNOP()
    return fBound
}
相關文章
相關標籤/搜索