对于这种情况,我对箭头函数中的 'this' 关键字感到困惑

I am confused with the 'this' keyword in arrow function for this case

我有两种情况,我对得到的输出感到困惑。

案例一:

let x = {
  b: 5,
  y: function a() {
    return function() {
      return () => {
        console.log(this);
      }
    }
  }
}
x.y()()();

在 运行 x.y()()() 我得到 Window 对象作为输出,但根据箭头函数的定义,输出应该是它的父函数。

案例二:

let x = {
  b: 5,
  y: function a() {
    return () => {
      console.log(this);
    }
  }
}
x.y()();

如果我删除一层嵌套,这是一个基本示例,那么在 运行 x.y()() 上,我将得到对象 x 作为输出。

你能解释一下为什么我会得到这些输出吗?

第一个函数调用是

x.y()

在该函数调用中,this 将引用 x。该函数调用returns一个函数,在第二个函数调用中使用:

x.y()()

函数调用没有明确的 this 值,因此 thiswindow。该函数调用 returns 另一个 函数,最里面的箭头函数,根据其定义从其闭包中继承 window 作为 this。因此第三个函数调用:

x.y()()()

也有 window 作为 this 的值。

函数内部的 this 由其调用上下文以及函数是否为箭头函数决定。

使用箭头函数,this 总是从外部作用域继承。

如果被调用的函数是完整的 function 则该函数被调用为 属性 作为对象(例如 obj.foo()),那么调用上下文,foo函数里面的this,就是obj.

如果被调用的函数是完整的 function 独立的 - 也就是说,不是对象的 属性 - 那么就没有调用上下文,函数内部的 this 将是 undefined 或全局对象。

let x = {
  b: 5,
  y: function a() {
    return function() {
      return () => {
        console.log(this === window);
      }
    }
  }
}
x.y()()();

this 是返回函数的 this 是什么 - 也就是说,这里调用的函数的调用上下文:

x.y()()

被调用的函数不是对象的一部分 - 它是一个独立的函数表达式,它是通过在对象上调用 prior 函数创建的,等同于:

(x.y())()

所以,没有调用上下文,this是全局对象。

相反,在情况2中,最近的祖先function被调用是对象y属性上的a函数,而这个函数调用的调用上下文:

x.y()()
^^^^^

上面,y 是用 x 的调用上下文调用的,所以 thisa 函数中是 x

你的问题与这个问题类似:-Javascript "this" pointer within nested function

在父对象内部调用函数 - 在 x.y() 的情况下,它有一个领先的父 x,这就是它 returned x 的原因 - 在 x.y()() & x.y()()() 的情况下,内部函数被调用,它没有前导父对象,因此将 return window.

这是怎么回事...

首先,让我们稍微修改一下您的代码——但在我们这样做之前,让我们谈谈 甚至影响 this 的因素:"CallSite"。

CallSite

调用函数的位置将决定它的 this 值,并且有一个 先例顺序 伴随着这种决定。基本上,有 4 种类型的 CallSite,然而,有 1 或 2 个警告可以说是这 5 或 6 种类型。

4 种 CallSite

  • 全球
  • 隐式
  • 显式
  • 新建
全球的

这是相当明显和直接的...调用一个与任何对象分离的函数,this 将是 全局作用域:

function run() {
  this === window === true;
}
隐含的

也相当简单。将任何函数作为对象的方法调用,this 将等于该对象的实例。

function run() { return this; }
run();  // Global
({ id: 'test', run: run }).run();  // > { id: 'test', run: f() }
明确的(见下面的警告)

直截了当。使用 Function.prototype.callFunction.prototype.apply 调用函数将 强制 this 等于原型方法的第一个参数。但是,这一规则有一个警告 -- 敬请期待。

(function run() { return this; }).call(new Klass());  // this instanceof Klass === true
新的

尽可能简单明了。调用函数并在调用前加上 newthis 将等于隐含的 class 的新实例。但是,这确实有一个例外。

CallSite 优先级注意事项

明确的 如果函数是调用 Function.prototype.bind 的结果,

Function.prototype.call & Function.prototype.apply 不能强制函数的上下文。此外,值得注意的是 调用 Function.prototype.bind 已经绑定的函数不会重新绑定到另一个上下文;其 this 值将与原始 bind 调用.

的值保持相同
function run() { return this; }
let o = { };
let x = { };
let r = run.bind(o);
let result = r.call(x);  // result === x === false && result === o === true
新的

当调用以 new 为前缀的函数时,如果函数 return 是不同的,则您将不会获得隐含 class 的对象实例值.

class Class {}
var Klass = function Klass() {
    return new Class;
};
var klass = new Klass();  // klass instanceof Class === true

您的代码(已修改)

在您的原始代码中,您得到了 x 的值,因为 Arrow-Functions 总是将最外层的作用域渗入到它们自己的作用域中 。也就是说,箭头函数将始终采用其 父作用域的 this 上下文 。此行为 与 return 在函数上调用 Function.prototype.bind 并提供 this 作为其上下文 .

的结果没有什么不同
var x = {
  b: 5,
  y: function a() {
    console.log('a', this);
    return function b() {
      console.log('b', this);  // window
    }
    return () => {
      console.log('=>', this);  // x
    }
  }
}
x.y()();

当我们调用 x.y() 时,我们正在 returning 那个函数 return。我修改了您的代码以呈现两个 return 场景:

  • 返回 function b() {...}x.y() 是一个分离函数。因此,它的 CallSite 将是 Global
  • 返回 () => {...}x.y() 是一个分离的箭头函数。它的行为就像您在规范函数上调用 bind 并将 [parent] 函数的 this 上下文作为第一个参数传递。

关闭

我希望在进一步了解 CallSite 之后这会更有意义。知道了这一点,您就会意识到可以实际使用 new 作为调用策略。也就是说,尝试找出以下代码如何 运行 成功:

练习 1("Crockford Dog Balls Problem")

以下两行代码表现出什么行为?

var a = new (function Klass() {})();  // a
var b = new (function Klass() {}());  // b

练习 2 ("What's New?")

编写一个函数来修复以下代码 运行 成功。

var instance = new new new new Klass();

希望对您有所帮助!