箭头函数和这个

IT技术 javascript ecmascript-6
2021-01-29 03:21:11

我正在浏览twitter,发现了这条推文:

https://twitter.com/_ericelliott/status/855598297939144704

这是推文中的代码:

const double = () => this.x * 2;
const numDouble = double.bind({ x: 5 });
numDouble();

当您在控制台中运行此代码段时,它会生成 NaN。如何?作者明确绑定了 x 的值,但它仍然显示 NaN。

作者还指定箭头函数不能绑定 this。据我所知,箭头函数在词法上绑定了围绕作用域的这种形式的值。那作者为什么这么说呢?

请澄清我的疑问,并在此先感谢您的帮助。

2个回答

箭头函数不绑定this根据 MDN:

没有绑定这个

在箭头函数之前,每个新函数都定义了自己的this值(在构造函数的情况下是一个新对象,在严格模式函数调用中是未定义的,如果函数被称为“对象方法”则是上下文对象,等等)。事实证明,这对于面向对象的编程风格来说很烦人。

所以this在你的例子中将是全局对象window,它显然没有名为x.

例子:

function foo() {
  let arrow = () => {
    console.log(this);     // will use foo's this as arrow will never have its own this
  }
  
  arrow.call({"x": "x"});  // ... even if we specify it using bind, call, or apply
}

foo.call({"y": "y"});      // specifying the this for foo (this value will eventually be used by arrow because it will be availbale in its scope)

所以根据 MDN,箭头函数不绑定 this,他们也没有自己的 this。
2021-03-16 03:21:11
@KishanPatel 没错!'this' 是 undefined我想你的意思是And 'x' is undefined检查上面的例子!
2021-03-18 03:21:11
你能再澄清一点吗?
2021-03-23 03:21:11
@KishanPatel 箭头函数就像浅层函数。他们没有自己的this(即使bind他们不会)。他们只是使用他们this所在范围内可用的任何内容。我将发布一个示例。
2021-03-30 03:21:11
哦..这就是为什么在我们的例子中'this'回退到全局对象。并且“this”在全局窗口中未定义,如果您尝试将其与 2 相乘,它将显示 NaN。感谢您澄清我的疑虑。
2021-04-11 03:21:11

要记住的关键是:

  • 箭头函数close over this,正是函数关闭变量的方式。 (实际上,这是相同的机制。)无论this在何处创建箭头函数,this在调用该箭头函数期间都会发生什么它永远不会是其他任何东西。箭头函数忽略this它们被调用的。

如果您记住了这一点,您就再也不会被this箭头函数所迷惑了。

当您在控制台中运行此代码段时,它会生成 NaN。如何?作者明确绑定了 x 的值,但它仍然显示 NaN。

numDouble = double.bind({ x: 5 })创建一个新函数 ( numDouble),当调用该函数时,它将调用原始函数 ( double) 并this设置为您作为bind第一个参数 ( { x: 5 })提供的值但是由于箭头函数忽略了this它们的调用方式,bind因此无法控制this它们使用的内容。

作者还指定箭头函数不能绑定 this。据我所知,箭头函数在词法上绑定了围绕作用域的这种形式的值。

是的,这意味着你不能改变它。词法绑定是闭包的工作方式。这个箭头函数:

const a = () => {
    console.log(typeof this);
};

治疗this完全相同的方式这种传统的功能对待thisWhereFunctionWasCreated

const thisWhereFunctionWasCreated = this;
const t = function() {
    console.log(typeof thisWhereFunctionWasCreated);
};

就像调用thisWhereFunctionWasCreated变量t时无法更改其this a用途一样,调用它时也无法更改其用途。(如果thisWhereFunctionWasCreated不是 a const,您可以更改它所持有,但不能更改使用哪个thisWhereFunctionWasCreated变量 t。但在该示例中它是一个常量,因为this是一个常量。)

由于箭头函数完全忽略了this调用它的方法,因此无论您使用什么机制来尝试告诉箭头函数this使用什么,它都不会起作用。无论您是this通过将函数调用为方法 ( obj.arrow()) 还是通过callapply( arrow.call(obj)) 或通过bind( const boundArrow = arrow.bind(obj); boundArrow();) 来隐式指定,它仍将使用this它关闭的方法:

"use strict";

function Ctor() {
    
    // `this` will be the object created by `new Ctor`; grab it
    this.name = "outerThis";
    const outerThis = this;
    
    // `traditional` doesn't close over `this`, so you CAN change
    // what `this` it uses when you call it, in various ways
    function traditional(testNum) {
        console.log(testNum, "traditional:", getName(this));
    }
    
    // `arrow` closes over `this`, so you CAN'T change
    // what `this` it uses when you call it
    const arrow = testNum => {
        console.log(testNum, "arrow:      ", getName(this));
    };

    // Remember that the `this` in a direct call is the global
    // object in loose mode, `undefined` in strict mode; this
    // code is in strict mode
    console.log("Direct call (default `this`):");
    traditional(1);              // 1 traditional: window
    arrow(1);                    // 1 arrow:       outerThis
    
    console.log("`obj.xyz()`:");
    const obj = {
        name: "obj",
        arrow,
        traditional
    };
    obj.traditional(2);          // 2 traditional: obj
    obj.arrow(2);                // 2 arrow:       outerThis

    console.log("Using `call`:");
    traditional.call(obj, 3);    // 3 traditional: obj
    arrow.call(obj, 3);          // 3 arrow:       outerThis

    console.log("Using `bind` and calling result:");
    const boundTraditional = traditional.bind(obj);
    const boundArrow = arrow.bind(obj);
    boundTraditional(4);         // 4 traditional: obj
    boundArrow(4);               // 4 arrow:       outerThis
}

function getName(t) {
    switch (t) {
        case undefined:
            return "undefined";
        case window:
            return "window";
        default:
            return t.name;
    }
}

new Ctor();
.as-console-wrapper {
    max-height: 100% !important;
}

bind调用箭头函数时唯一可以做的就是将参数绑定到它:

const arrow = (x, y) => x + y;
console.log(arrow(2, 3));      // 5

const arrowWith2 = arrow.bind(null, 2);
console.log(arrowWith2(3));    // 5

const arrowWith2And3 = arrow.bind(null, 2, 3);
console.log(arrowWith2And3()); // 5

(它还将结果函数的名称设置为"bound x"[wherex是原始函数的名称。所以arrowWith2.name在上面的是"bound arrow"。)