前言
聽說bind有點特別啊,一開始初學不知道,以爲那麼簡單的東西就綁成函數等待執行唄,後來發現這玩意實際還是比較複雜的。
mdn上Polyfill
if ( ! Function. prototype. bind) ( function ( ) {
var slice = Array. prototype. slice;
Function. prototype. bind = function ( ) {
var thatFunc = this , thatArg = arguments[ 0 ] ;
var args = slice. call ( arguments, 1 ) ;
if ( typeof thatFunc !== 'function' ) {
throw new TypeError ( 'Function.prototype.bind - ' +
'what is trying to be bound is not callable' ) ;
}
return function ( ) {
var funcArgs = args. concat ( slice. call ( arguments) )
return thatFunc. apply ( thatArg, funcArgs) ;
} ;
} ;
} ) ( ) ;
前面有個註釋,這個不支持在用new來生成綁定函數。也就是說,原版的bind可以new的。
當然原版還有很多特別的地方。寫幾個小例子測試下原版。
原版測試
function aaa ( ) { }
ccc = aaa. bind ( 'ddd' )
console. log ( ccc. __proto__)
console. log ( ccc. constructor)
console. log ( ccc. prototype)
這個就是隻有一個函數然後直接bind情況。這3個打印分別是Function.prototype,Function,undefined。
就是這個undefined表示了函數生成的bind函數,無prototype。
這好像暗示我們bind內部用箭頭函數實現,但是,箭頭函數不能new啊。
使用傳參形式結合new看看效果:
function aaa ( x, y) {
this . x= x
this . y= y
}
aaa. prototype. getXY = function ( ) {
return this . x+ this . y
}
let bindfunc = aaa. bind ( null , 11 )
let ins = new bindfunc ( 33 )
console. log ( ins. getXY ( ) )
console. log ( ins instanceof bindfunc )
console. log ( ins instanceof aaa )
console. log ( ins. constructor)
console. log ( bindfunc. constructor)
console. log ( bindfunc. __proto__)
console. log ( bindfunc. prototype)
通過console其實可以推斷,bind所返回的函數的prototype,應該是能通過原型鏈找到原函數的prototype的。而且如果new的話,this不是指向綁定this,而是指向實例本身(這個就有點跟bind設計的意思衝突,用bind想改變this,結果new的時候this又不改變)。
正常的函數,new出來實例的constructor會指向這個函數。所以正常來說,這個Ins的constructor應該指向bindfunc纔對。所以bindfunc的prototype被調換過了。
看一下mdn推薦的大神寫的bind打印結果也和真正的bind不一樣。大神寫的bind地址
用上面那個例子測試大神寫的這個bind,會發現打印結果:
44
true
true
[Function: aaa]
[Function: Function]
[Function] { mybind: [Function: mybind] }
aaa {}
大神除了undefined問題,其他基本上都跟原版打印的一樣。
實現bind
對於這種問題,最好就是畫個圖,然後照着圖連就行了。這裏我畫了個圖:
可以發現,如果用Js實現bind,undefined問題是無法避免的,因爲你能new,而且必須讓實例找到原函數上,就得設置bindfunc的prototype。不然你new出來的實例找不到路。雖然js可以通過給bindfunc設置代理,讓其prototype的打印值爲undefined,但是這樣你返回的函數就是代理對象,而不是原來那個綁定函數。
我們可以通過instanceof 判斷是不是被new調用,如果是new 調用,那麼外面那個this是原函數,裏面那個this是new出來的對象。
最後可以把前面大神寫的簡略點,改成這樣:
Function. prototype. mybind = function ( context, ... args) {
let fn = this
if ( typeof fn !== 'function' ) {
throw 'error'
}
let fbound = function ( ... innerArgs) {
return fn. apply (
this instanceof fn ? this : context, [ ... args, ... innerArgs]
)
}
fbound. prototype = Object. create ( fn. prototype)
return fbound
}
function aaa ( x, y) {
this . x = x
this . y = y
}
aaa. prototype. getXY = function ( ) {
return this . x + this . y
}
let bindfunc = aaa. mybind ( null , 11 )
let ins = new bindfunc ( 33 )
console. log ( ins. getXY ( ) )
console. log ( ins instanceof bindfunc )
console. log ( ins instanceof aaa )
console. log ( ins. constructor)
console. log ( bindfunc. constructor)
console. log ( bindfunc. __proto__)
console. log ( bindfunc. prototype)