以下代码片段中“this”值变化的解释?

An explanation for the change in the value of `this` in the following snippet of code?

function wrap(func) {
  console.log('0', this)
  return function x() {
    console.log('1', this)
    func()
    return function z() {
      console.log('3', this)
    }
  }
}

var obj = {
  x: 5,
  test: wrap(function y() {
    console.log('2', this)
  })
}

obj.test()()

上面的代码记录

// 0 Window
// 1 Object
// 2 Window
// 3 Window

我无法理解什么决定了 this 的值应该是什么。

// 0 Window 是有道理的,因为这是第一次调用 wrap 的时候,此时它的值 this 不应该设置为 obj

// 1 Object 有意义 obj.test 现在等于 function x(),它正确地将 this 记录为 obj

// 2 Window 我不确定为什么会这样。为什么 this 的值不会传播到 func()?我认为 this 应该指的是函数的所有者。这是否意味着即使 function y() 是在 obj 内部创建的,它也会以某种方式被提升到 window?

// 3 Window 同样 function z() 为什么 this 的值没有从 function x() 传下来。 this 的值不应该在那里传播吗?

'this' 绑定非常混乱。但是,如果您记住以下这些规则,就会更容易理解。根据 Javascript 文档,'this' 绑定的顺序有四个规则:

  1. 新运营商
  2. 通过调用或应用方法进行显式或硬绑定
  3. 与包含对象的隐式绑定,如对象的方法
  4. 默认(全球)

在您的脚本中,只有规则 #3 和 #4 适用,因为没有新的运算符和 call 或 apply 方法。

所以解释是:

// 0 Window - 规则 #4 默认(全局)

// 1 对象 - 规则 #3 与包含对象的隐式绑定,就像一个对象的方法,obj.test 在你的例子中

// 2 Window - 规则 #4 默认(全局)

// 3 Window - 规则 #4 默认(全局)

以下,this文章的大部分准确指导,按顺序,函数调用desugar到

  • wrap.call(window, function() {})
  • obj.test.call(obj)
  • y.call(window)
  • z.call(window)

要记住的是 this 不会 通过函数声明传播。 this 的值完全取决于函数的调用方式。

我之所以感到困惑,是因为我对箭头函数有点太习惯了,箭头函数为您保留了 this 的值。

function wrap(func) {
  return () => {
    console.log(this)
    return () => {
      console.log(this)
      func()
      return () => console.log(this)
    }
  } 
}

这个脱糖到

function wrap(func) {
  var _this = this;

  return function () {
    console.log(_this);
    return function () {
      console.log(_this);
      func()
      return function () {
        return console.log(_this);
      };
    };
  };
}

并显示您必须显式传递 this 的值。即使使用箭头函数,重要的是要注意对 func 的调用不会保留 this 的值。我需要做 func.call(this) 才能做到这一点。