this你到底跑谁家了

19 阅读1分钟

题目A

let gg = this
let foo = function() {
  console.log(this, gg);
  let that = this
  return function() {
    for(let i = 0; i < 1; i++) {
      console.log(this, that);
    }
  }
}
foo()()

题目B

let gg = this
let foo = function() {
  console.log(this, gg);
  let that = this
  return () => {
    for(let i = 0; i < 1; i++) {
      console.log(this, that);
    }
  }
}
foo()()

题目AA

let gg = this
let foo = function() {
  console.log(this, gg);
  let that = this
  return function() {
    for(let i = 0; i < 1; i++) {
      console.log(this, that);
    }
  }
}
foo = foo.bind({t:'t'})
foo()()

题目BB

let gg = this
let foo = function() {
  console.log(this, gg);
  let that = this
  return () => {
    for(let i = 0; i < 1; i++) {
      console.log(this, that);
    }
  }
}
foo = foo.bind({t:'t'})
foo()()

试问,这4道题,在Node中、浏览器中的输出是多少?

如果能准确说出结果,恭喜你,你的闭包、变量作用域、箭头函数this等知识还挺牢固。

浏览器中

  • 题目A: window window, window window
  • 题目B: window window, window window
  • 题目AA:{t:'t'} window, window {t:'t'}
  • 题目BB:{t:'t'} window, {t:'t'} {t:'t'}

Node中

  • 题目A: global {}, global global
  • 题目B: global {}, global global
  • 题目AA: {t:'t'} {}, global {t:'t'}
  • 题目BB: {t:'t'} {},{t:'t'} {t:'t'}

解释

  1. 谁调用的,this就指向谁
  2. 找不到谁调用的,this指向window/global
  3. 箭头函数为上一个作用域的this

初始作用域

  • 浏览器在执行脚本时的初始作用域(Script)的this为window
  • Node在执行文件时,会使用包装器函数包装一层以提供module require exports等等,该函数的this为{}

总的来说

浏览器会有以下形式的作用域链

window Script(初始域this为window) Closure*(闭包链) Local(当前函数域) Block(for中let形成的域)

image.png

当以module方式引入时

window Module(初始域this为undefined) Closure*(闭包链) Local(当前函数域) Block(for中let形成的域)

Node会有以下作用域链

global Closure(初始域this为{})  Closure*(闭包链) Local(当前函数域) Block(for中let形成的域)

image.png

附加题P

let gg = this
let foo = function() {
  console.log(this, gg);
  let that = this
  return function() {
    for(let i = 0; i < 1; i++) {
      console.log(this, that);
    }
  }
}
foo = foo.bind({t:'t'})
foo().bind({x:'x'})()

附加题PP

let gg = this
let foo = function() {
  console.log(this, gg);
  let that = this
  return () => {
    for(let i = 0; i < 1; i++) {
      console.log(this, that);
    }
  }
}
foo = foo.bind({t:'t'})
foo().bind({x:'x'})()

以上两个解释了箭头函数和普通函数的区别,普通函数会找谁调用的它,而箭头函数会找上一层作用域中的this,所以在node中P的结果为{t:'t'} {}, {x:'x'} {t:'t'},PP的结果为{t:'t'} {}, {t:'t'} {t:'t'}.

bind做了什么

image.png image.png

对比原函数,我们可以看到添加了[[BoundThis]]和[[BoundArgs]]两个属性,从而替换this

vue2中实例的函数

image.png

同样可以看到,vue2对methods中的函数绑定了this,所以当组件间传递函数时,函数执行时其中的this仍是原组件实例。