bind是什么?
bind()方法會(huì)創(chuàng)建一個(gè)新函數(shù)淳地。當(dāng)這個(gè)函數(shù)被調(diào)用的時(shí)候怖糊,bind()的第一個(gè)參數(shù)將作為它運(yùn)行時(shí)的this,之后的一序列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為它的函數(shù)颇象。
所有bind函數(shù)具有兩個(gè)特點(diǎn):
- 返回一個(gè)函數(shù)
- 可以傳入?yún)?shù)
var foo = {
value: 1
}
function bar(){
console.log(this.value)
}
var bindFoo = bar.bind(foo)
bindFoo() // 1
第一版模擬實(shí)現(xiàn)
Function.prototype.bind2 = function(context) {
var self = this
return function() {
self.apply(context)
}
}
傳參的模擬實(shí)現(xiàn)
bind參數(shù)需要注意兩點(diǎn):
- 在bind的時(shí)候伍伤,是可以進(jìn)行參數(shù)傳遞
- 執(zhí)行bind的返回函數(shù)的時(shí)候,也是可以進(jìn)行參數(shù)傳遞的
var foo = {
value: 1
}
function bar(name,age) {
console.log(this.value)
console.log(name)
console.log(age)
}
var bindFoo = bar.bind(foo, 'kack')
bindFoo(18) // 1 'kack' 18
這種情況下遣钳,我們可以使用arguments來(lái)進(jìn)行處理:
Function.prototype.bind2 = function(context) {
var self = this
var args = Array.prototype.slice.call(arguments, 1)
console.log(args)
return function() {
var bindArgs = Array.prototype.slice.call(arguments)
console.log(bindArgs)
self.apply(context, args.concat(bindArgs))
}
}
構(gòu)造函數(shù)的模擬效果實(shí)現(xiàn)
bind還有一個(gè)特點(diǎn):
一個(gè)綁定函數(shù)也能使用new操作符創(chuàng)建對(duì)象:這種行為就像把原函數(shù)當(dāng)做成構(gòu)造器扰魂。提供的this值被忽略,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)蕴茴。
也就是說(shuō)當(dāng)bind返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候劝评,bind時(shí)指定的this值會(huì)失效,但傳入的參數(shù)依然生效倦淀。
var value = 2
var foo = {
value: 1
}
function bar(name, age) {
this.habit = 'shopping'
console.log(this.value)
console.log(name)
console.log(age)
}
bar.prototype.friend = 'kevin'
var bindFoo = bar.bind(foo, 'daisy')
var obj = new bindFoo(18)
// undefined
// daisy
// 18
console.log(obj.habit) // shopping
console.log(obj.friend) // kevin
盡管在全部和foo中都定義了value的值蒋畜,最后依然返回了undefined,說(shuō)明綁定的this失效晃听,此時(shí)的this已經(jīng)指向了obj百侧。
Function.prototype.bind2 = function(context) {
var self = this
var args = Array.prototype.slice.call(arguments, 1)
// 考慮到直接修改fbound的prototype,會(huì)直接修改原函數(shù)的prototype能扒,可以通過(guò)一個(gè)函數(shù)來(lái)中轉(zhuǎn)下
var fNOP = function(){}
var fbound = function() {
var bindArgs = Array.prototype.slice.call(arguments)
// 考慮兩種情況
// 1佣渴、作為構(gòu)造函數(shù)時(shí),this指向?qū)嵗醢撸瑂elf指向綁定函數(shù)
// 2辛润、作為普通函數(shù)時(shí),this指向window见秤,self指向綁定函數(shù)
self.apply(this instanceof self ? this : context,args.concat(bindArgs))
}
// 修改返回函數(shù)的prototype為綁定函數(shù)的prototype砂竖,實(shí)例就可以繼承函數(shù)的原型中的值
fNOP.prototype = this.prototype
fbound.prototype = new fNOP()
return fbound
}
bind函數(shù)模擬的優(yōu)化實(shí)現(xiàn)
- 調(diào)用bind的不是函數(shù)情況
- 兼容性處理
最終代碼如下:
Function.prototype.bind2 = Function.prototype.bind || function (context) {
// 兼容性處理
if(typeof this !== 'function') {
throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
}
var self = this
var args = Array.prototype.slice.call(arguments, 1)
// 考慮到直接修改fbound的prototype,會(huì)直接修改原函數(shù)的prototype鹃答,可以通過(guò)一個(gè)函數(shù)來(lái)中轉(zhuǎn)下
var fNOP = function() {}
var fbound = function () {
var bindArgs = Array.prototype.slice.call(arguments)
// 考慮兩種情況
// 1乎澄、作為構(gòu)造函數(shù)時(shí),this指向?qū)嵗馑ぃ瑂elf指向綁定函數(shù)
// 2置济、作為普通函數(shù)時(shí)解恰,this指向window,self指向綁定函數(shù)
self.apply(this instanceof self ? this : context, args.concat(bindArgs))
}
// 修改返回函數(shù)的prototype為綁定函數(shù)的prototype浙于,實(shí)例就可以繼承函數(shù)的原型中的值
fNOP.prototype = this.prototype
fbound.prototype = new fNOP()
return fbound
}