【Javascript】探究bind()的作用及实现原理
AwesomeDevin opened this issue · comments
Devin Deng commented
bind()的作用
在js中,我们通常使用bind()
来修改this指向
bind()
方法创建一个新的函数,在bind()
被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
我们来看1个demo
var module = {
x: 42,
getX: function() {
return this.x;
}
}
module.getX.prototype.sayHi = function(name){
return 'I am ' + name
}
var unboundGetX = module.getX
console.log(module.getX(),new module.getX().sayHi('module.getX')) // 42 "I am module.getX"
console.log(unboundGetX(),new unboundGetX().sayHi('unboundGetX')) // undefined "I am unboundGetX"
unboundGetX()的this指向window,所以输出this.x为undefined
,现在,我们使用bind来修改unboundGetX()的this指向
var boundGetx = module.getX.bind(module) //this指向变量module
console.log(boundGetx(),new boundGetx().sayHi('boundGetx')) //42 "I am boundGetx"
可以看到bind()返回的函数,其this指向已经指向了module,this.x为=42
不了解this作用域的同学可以先看一下这篇文章【Javascript】深入理解this作用域问题并探究new/let/var/const对this作用域的影响
bind()的实现原理
现在,我们来尝试实现一个newbind(),使得boundGetx的this指向变量module
var boundGetx = module.getX.newbind(module)
newbind()实现
Function.prototype.newbind = function(){
const oThis = Array.prototype.shift.call(arguments) // 获取参数module
const params = Array.prototype.slice.call(arguments)
oThis.fn = this //函数function
const res = function(){
return oThis.fn.apply(
oThis,params.concat(Array.prototype.slice.call(arguments))
) //修改函数function的this指向并执行
// return oThis.fn(...params.concat(Array.prototype.slice.call(arguments))) //一样
}
if(this.prototype)
{
const fn = function(){}
fn.prototype = this.prototype //维护原型关系
res.prototype === new fn()
}
return res
}
此时newbind已经返回了1个新的函数
验证newbind正确性
var unboundGetX = module.getX
var boundGetx = module.getX.newbind(module)
console.log(module.getX(),new module.getX().sayHi('module.getX')) // 42 "I am module.getX"
console.log(unboundGetX(),new unboundGetX().sayHi('unboundGetX')) // undefined "I am unboundGetX"
console.log(boundGetx(),new boundGetx().sayHi('boundGetx')) //42 "I am boundGetx"
可以发现unboundGetX()仍然输出undefined,但是boundGetx()其this已经指向了module,this.x为42
注意事项
res.prototype = this.prototype
存在问题,由于对象属于引用类型,这样的写法会导致module.getX.prototype会随着boundGetx.prototype
改变,我们尝试修改一下boundGetx.prototype
boundGetx.prototype.sayHi = function(name){
return 'changed,I am ' + name
}
console.log(new boundGetx().sayHi('boundGetx')) // changed,I am boundGetx
console.log(new module.getX().sayHi('module.getX')) //changed,I am module.getX
可以发现
boundGetx()
与module.getX()
的sayHi()
都被改变了,我们需要将代码改为
res.prototype = new this() //维护原型关系
// or
// const fn = function(){}
// fn.prototype = this.prototype
// res.prototype = new fn()
bind与call,apply的差别
bind返回的是1个函数,call,apply返回值为undefined
Function.prototype.call = function(obj,...args){
obj.fn = this
obj.fn(...args)
delete obj.fn
}
完整代码
Function.prototype.newbind = function(){
const oThis = Array.prototype.shift.call(arguments) // 第1个参数
const params = Array.prototype.slice.call(arguments)
oThis.fn = this //函数function
const res = function(){
return oThis.fn.apply(
oThis,params.concat(Array.prototype.slice.call(arguments))
) //修改函数function的this指向并执行
}
if(this.prototype)
{
res.prototype = new this() //维护原型关系
}
return res
}
var module = {
x: 42,
getX: function(val) {
return val ? val : this.x
}
}
module.getX.prototype.sayHi = function(name){
return 'I am ' + name
}
var unboundGetX = module.getX;
//使用bind修改unboundGetX的this指向
var boundGetx = module.getX.newbind(module)
console.log(module.getX(),new module.getX().sayHi('module.getX')) //this指向变量module
console.log(unboundGetX(),new unboundGetX().sayHi('unboundGetX')) //this指向window,所以为undefined
console.log(boundGetx(),new boundGetx().sayHi('boundGetx')) //this指向变量modul
console.error('---------sayHi was changed------')
boundGetx.prototype.sayHi = function(name){
return 'changed,I am ' + name
}
console.log(new boundGetx().sayHi('boundGetx'))
console.log(new module.getX().sayHi('module.getX'))