Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Javascript】探究bind()的作用及实现原理 #25

Open
AwesomeDevin opened this issue Aug 22, 2019 · 0 comments
Open

【Javascript】探究bind()的作用及实现原理 #25

AwesomeDevin opened this issue Aug 22, 2019 · 0 comments

Comments

@AwesomeDevin
Copy link
Owner

AwesomeDevin commented Aug 22, 2019

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
}

完整代码

codesandbox 运行代码

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'))

@AwesomeDevin AwesomeDevin changed the title 【Javascript】探讨bind的作用及实现原理 【Javascript】探究bind的作用及实现原理 Aug 22, 2019
@AwesomeDevin AwesomeDevin changed the title 【Javascript】探究bind的作用及实现原理 【Javascript】探究bind()的作用及实现原理 Aug 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant