美文网首页
bind() 和 箭头函数的this

bind() 和 箭头函数的this

作者: sunny519111 | 来源:发表于2017-09-22 19:47 被阅读811次

箭头函数的This指向

更新 2017年11月26号


前言

楼主在昨天在看Vue文档的时候,主要到methodscomputed里面不要使用箭头函数,去看了下源码解析,发现里面调用的是通过自定义的bind函数,通过call()来执行函数以及绑定作用域,想巩固一下箭头函数,于是这篇有内涵的blog就上线了。

之前楼主有一篇箭头函数的This, 对于它的理解感觉有偏差,这里全部再重复总结一遍。

看完本篇文章,你可以彻底了解this和bind

涉及知识点

  1. bind函数的深入了解解析
  2. 作用域
  3. thisArg

实现一个bind函数

var fn = function(a,b,c,d) {
  return a+b+c+d ;
}
fn.bind(scope,a,b)(c,d)
// 调用方式  var fn1 = fn.bind(scope,a,b)  fn1(c, d)
// scope是传递进来的this
Function.prototype.bind = function(scope) {
  let newFn = this;
  // 获取通过bind传递的参数
  let args = Array.prototype.slice.call(arguments,1)
  let fbind = function() {
    // 这里执行函数,通过闭包绑定了this===scope
    // 然后通过concat合并2个参数
    return newFn.apply(scope,args.concat(Array.prototype.slice.call(arguments)))
  }
  return fbind
}

调用方式:

  1. 直接调用 (内部this一般是window)
  1. 构造函数的调用,通过bind绑定的this无效 (this是实例对象)

当使用构造函数的时候,我们需要构建原型链,所以需要加工一下。

Function.prototype.bind = function(scope) {
  if( typeof this !== 'function') {
    throw('this is illegal')
  }
  let newFn = this;
  // 获取通过bind传递的参数
  let args = Array.prototype.slice.call(arguments,1)
  let fbind = function() {
    // 里面的this可能是window和构造函数实例
    // 然后通过concat合并2个参数
    return newFn.apply(this instanceof fbind ? this : scope              ,args.concat(Array.prototype.slice.call(arguments)))
  }
  // 维持原型链
  if(this.prototype) {
    fbind.prototype = this.prototype
  }
  return fbind
}
// 
function Fn(a,b) {
  this.a = a
  this.b = b
}

Fn.prototype = function() { console.log(this)}
let hcc = Fn.bind({a:1})
// 直接调用
hcc()  // 返回一个新函数fbind,  fbind里面的this则是window
// 所以 this instanceof fbind 为 false 

// 构造函数的调用
let obj = new hcc() // fbind里面的this则是fbind的实例
// 所以 this instanceof fbind 为 true ,所以改变this没有生效 

解析 : 当我们调用bind()的时候,即执行了var fn1 = fn.bind({name: 1},1,3), 会返回一个新的函数,下面是作用域链解析。

实例 : 当我们调用bind()的时候,即执行了var fn1 = fn.bind({name: 1},1,3), 会返回一个新的函数,下面是作用域链解析。

bind => function(scope) {
  let newFn = fn;
  let args = [1,3]
  let fbind = function() {
    return newFn.apply(scope,[1,3].concat(Array.prototype.slice.call(arguments)))
  }
  return fbind
}

fn1 =>  function() {
    return fn.apply({name: 1},[1,3].concat(Array.prototype.slice.call(arguments)))
}

再调用fn1(3,4),相当于执行函数

 fn.apply({name: 1},[1,3].concat(Array.prototype.slice.call(arguments)))
 // 即相当于这样执行
 fn.apply({name: 1},[1,3,3,4]) =>  11

思考,下面函数执行时多少,this是什么

var fn = function(a,b) {
  console.log(this)
  return a+b ;
}
fn.bind({name:1},1,2).call({name:2},3,4)  

答案 {name:1} 3

//fn.bind({name:1},1,2)  返回xxx
function xxx() {
   return fn.apply({name: 1},[1,2].concat(Array.prototype.slice.call(arguments)))
} 

// xxx.call({name:2},3,4) 调用 xxx(绑定了xxx的this= {name:2})
// xxx里面通过apply调用已经制定了this的fn函数
 fn.apply({name: 1},[1,2,3,4]) // this => {name: 1} a=1, b=2 

所以当我们执行fn.bind({name:1},1,2).call({name:2},3,4) ,本质上call并不能改变bind的返回函数的this,只是改变了内部封装了一个函数(xxx)的this,这也是bind的this参数不能被重写的原因。

总结bind函数到底做了什么

fun.bind(thisArg[, arg1[, arg2[, ...]]])
// 简化版
Function.prototype.bind = function bind(self) {
  return function() { return fn.apply(self) }
}

一个函数(fn)使用函数原型链上面的bind函数的时候,传递this(thisArg)和参数进去,返回的是一个新函数(xxx),新函数内部调用的是通过apply调用原来的函数(fn)并制定原函数(fn)的this。用简单的代码表示就是:

function fn(a,b) {
  return a+b;
}
fn.bind({name:1},1,3)  相当于变成这样=> function xxx() {
  return fn.apply({name: 1},Array.prototype.slice.call(arguments));
}

箭头函数的this(定义时候的this)

一句话总结: 箭头函数的函数体内的this就是定义时候的this,和使用所在的this没有关系。

:在定义箭头函数的时候就已经绑定了this,可以理解为就是在定义的时候,通过bind函数进行强行绑定this。

案例一

var calculate = {
  array: [1, 2, 3],
  sum:() => {
    console.log(this === window) // => true
  }
};
// 当我们在定义sum是一个箭头函数的时候,还没有执行,内部已经绑定了this,而此时的this就是全局的window

//可以转换成
var calculate = {
  array: [1, 2, 3],
  sum:function() {
    console.log(this === window) // => true
  }.bind(this)
};

案例二

var calculate = {
  array: [1, 2, 3],
  sum() => {
    return () => {
      console.log(this === window) 
    }
  }
};
// 此时我们在写calculate.sum的时候,由于还没有执行,所有并不存在里面的箭头函数,当我们执行calculate.sum()才算生成了箭头函数,箭头函数就是在这个时候绑定this的,所有这里就会和怎么调用sum函数有关系了。

案例三

const App = new Vue({
    el: '#app',
    methods: {
        foo: () => {
            console.log(this) // undefined
        }
    }
})
// 如果我们在Vue的实例中的methods使用箭头函数,那么在定义的时候,箭头函数会自动绑定当前作用域的this,并不会是绑定实例中的this
// 初始化的时候,执行的initMethods中绑定了this(vm)
function initMethods (vm: Component) {
  const methods = vm.$options.methods
  if (methods) {
    for (const key in methods) {
      vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
      if (process.env.NODE_ENV !== 'production' && methods[key] == null) {
        warn(
          `method "${key}" has an undefined value in the component definition. ` +
          `Did you reference the function correctly?`,
          vm
        )
      }
    }
  }
}

//bind

function bind (fn, ctx) {
  function boundFn (a) {
    var l = arguments.length;
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)//通过返回函数修饰了事件的回调函数。绑定了事件回调函数的this。并且让参数自定义。更加的灵活
        : fn.call(ctx, a)
      : fn.call(ctx)
  }
  // record original fn length
  boundFn._length = fn.length;
  return boundFn
}

总结:我们知道Vue内部调用methods的时候,通过的call方法来执行methods中的相应的key函数,当我们使用箭头函数的时候,定义的时候就绑定了this,它源码中写的call()并不会被使用,所以必须不能使用箭头函数

Vue文档中methods的使用


参考文章

Vue methods 用箭头函数取不到 this

vue源码解析-事件机制

什么时候“不要”用箭头函数

ES6 箭头函数使用禁忌

推理例子

自己写的推理例子

相关文章

  • react性能优化 不要再component的props中使用箭头函数或者bind,因为每次使用箭头函数和bind...

  • React事件绑定

    1、在构造函数内使用bind绑定this 2、箭头函数绑定this 3、使用bind()绑定this 4、使用箭头...

  • bind() 和 箭头函数的this

    箭头函数的This指向 更新2017年11月26号 前言 楼主在昨天在看Vue文档的时候,主要到methods和c...

  • 箭头函数和普通函数的区别?

    1、 箭头函数比普通函数要简洁 2、箭头函数的this永远指向其上下文的this,call(), bind(), ...

  • this指向问题

    对比call 、bind 、 apply(对箭头函数无效)

  • 第2.4章:箭头函数

    新版写法:箭头函数 等价旧版写法:bind(this)

  • JSdeep(箭头函数)

    箭头函数 箭头函数本身没有this,它的this由父级作用域传入,不能作为bind,call参数传入 因此,箭头函...

  • ES6的箭头函数和普通函数区别

    刚才写下面一段代码时发现bind中this在箭头函数和普通函数指向不一样: 当在箭头函数是this指向window...

  • react 用es6写法 以及问题

    注意:点击事件,要使用bind(this) ,改变this指向 或者使用箭头函数

  • js prototype

    this 指向 箭头函数里的 this 指向声明函数的地方,也就是组件内,所以不用 bind(this)普通函数里...

网友评论

      本文标题:bind() 和 箭头函数的this

      本文链接:https://www.haomeiwen.com/subject/tdzfextx.html