【问题标题】:This in class method vs arrow function这在类方法与箭头函数
【发布时间】:2020-07-03 22:23:21
【问题描述】:

美好的一天

我最近遇到了一个奇怪的情况,我的 this 在类的成员函数中的值未定义。我知道有很多与未定义的this 上下文相关的问题,但我找不到这个问题的任何解释。我很想知道为什么会这样。在这个例子中,为什么setModified函数的箭头函数实现保留了它的this上下文,而类上的函数却没有。

这个例子打破了块类的setModified函数

class point {
    constructor(x, y) {
        this._x = x || 0;
        this._changeEvents = [];
    }

    set changeEvent(eventFunction) {
        this._changeEvents.push(eventFunction);
    }

    set x(value) {
        this._x = value;
        this.runChangeEvent();
    }

    set y(value) {
        this._y = value;
        this.runChangeEvent();
    }

    get x() {
        return this._x;
    }

    get y() {
        return this._y;
    }

    runChangeEvent() {
        this._changeEvents.forEach(event => event(this));
    }
}

class renderItem {
    constructor(canvas) {
        this._canvas = canvas;
    }

    render(){

    }
}

class block extends renderItem {
     constructor(canvas) {
        super(canvas);
        this._modified = true;
        this._topLeft = new point(0, 0);
        this._topLeft.changeEvent = this.setModified;
    }

    //Using a method on the class as a callback, it breaks
    setModified(){
         this._modified = true;//breaks, this is undefined
         console.log(this);
     }

    //Sets
    set topLeft(value) { this._topLeft = value; }

    //Gets
    get topLeft() { return this._topLeft }
}
//Creates an instance of the block
const bl = new block(null);
bl.topLeft.x = 20;

但是当您将setModified 函数更改为箭头函数(也在类上)时,它可以工作:

class point {
    constructor(x, y) {
        this._x = x || 0;
        this._y = y || 0;
        this._changeEvents = [];
    }

    set changeEvent(eventFunction) {
        this._changeEvents.push(eventFunction);
    }

    set x(value) {
        this._x = value;
        this.runChangeEvent();
    }

    set y(value) {
        this._y = value;
        this.runChangeEvent();
    }

    get x() {
        return this._x;
    }

    get y() {
        return this._y;
    }

    runChangeEvent() {
        this._changeEvents.forEach(event => event(this));
    }
}

class renderItem {
    constructor(canvas) {
        this._canvas = canvas;
    }

    render(){

    }
}

class block extends renderItem {
     constructor(canvas) {
        super(canvas);
        this._modified = true;
        //Using an arrow function on the class instance as a callback, it works
        this.setModified = () => {
            this._modified = true;//works
            console.log(this);
        };
        this._topLeft = new point(0, 0);
        this._topLeft.changeEvent = this.setModified;
    }

    //Sets
    set topLeft(value) { this._topLeft = value; }

    //Gets
    get topLeft() { return this._topLeft }
}
const bl = new block(null);
bl.topLeft.x = 20;

为什么类上的成员函数会丢失 this 上下文而不是箭头函数?

【问题讨论】:

  • this._topLeft.changeEvent = this.setModified; setModified 正在转移到另一个变量,该变量没有另一个变量作为属性。 this._modified 存在。 this._topLeft._modified 没有
  • 注意这里有很多与具体问题无关的代码。将来,如果您将其缩小到 minimal reproducible example 并删除任何不相关的内容,将会有所帮助
  • 在@Taplar的评论中添加,重新分配时可以拨打bind(this),防止丢失this。 this._topLeft.changeEvent = this.setModified.bind(this);
  • 哦...所以范围从块类变为点类,因为现在它是点类上的成员函数。我从来没有这样想过。因此,该函数现在将具有两个作用域或闭包,具体取决于调用它的位置。如果我理解正确?
  • 我重新创建了问题。 this 不绑定到 this._topLeft。实际上是undefined。从我在回答中引用的书中,在试图理解为什么会发生默认绑定之后:var bar = obj.foo; 然后调用bar() 即使bar 似乎是对obj.foo 的引用,实际上,它实际上只是另一个引用到foo 自己。此外,调用点很重要,调用点是bar(),这是一个普通的、未修饰的调用,因此适用默认绑定。我更新了我的答案,如果我说错了,请告诉我。

标签: javascript ecmascript-6 scope


【解决方案1】:

箭头函数表达式是一种语法紧凑的替代方案 一个正则函数表达式,虽然没有自己的绑定 this、arguments、super 或 new.target 关键字。

箭头函数的作用域与常规函数没有什么不同,但正如您在上面的MDN 中看到的,它们处理this 的绑定不同。

箭头函数不会创建自己对this 的绑定,实际上它继承自封闭范围(即类)。在类中,this._modified 存在。

箭头函数没有自己的 this。的这个值 使用封闭词法范围;箭头功能遵循正常 变量查找规则。

this 是在执行上下文中确定的,正如我们所展示的,箭头函数有效,因为this 与封闭范围的this 保持一致。但是功能正常...

但是在严格模式下,如果进入时没有设置 this 的值 一个执行上下文,它仍然是未定义的

如果您在函数中的错误之前console.log(this),您将看到它是undefined。但是为什么是undefined?阅读隐含丢失部分下的here。但基本上默认绑定是在调用它作为一个普通的、未修饰的函数引用时应用的,它来自赋值。而在严格模式下,this 将返回undefined,而不是在全局范围内。

在您的情况下不会发生隐式绑定,this 可能会隐式丢失。

这种绑定造成的最常见的挫折之一是 一个隐式绑定的函数会失去该绑定,这通常意味着 它回退到默认绑定,无​​论是全局对象还是 未定义,取决于严格模式。

如果您将runChangeEvent() 中的呼叫更改为event.call(this),您将看到this 实际上是point 的this(不是您想要的)。如果您致电this._topLeft.changeEvent = this.setModified.bind(this);,您将拥有您想要的范围。

调用 f.bind(someObject) 创建一个具有相同主体的新函数 和范围为 f,但在原始函数中出现这种情况时,在 新函数它永久绑定到 bind 的第一个参数, 不管函数是如何使用的。

查看该链接了解更多信息,我也推荐前面提到的online book。

【讨论】:

  • 我知道箭头函数继承了它们的“this”。但是在这个例子中,类上的箭头函数和成员函数都应该可以访问同一个“this”?
  • 你怎么称呼 setModified?
  • codesandbox.io/s/sweet-breeze-rxwod?file=/src/index.js我做了一个代码沙箱。这两种方法都在这里工作。这就是我问的原因。
  • 我现在明白了。哈哈,我怎么没想到。您的代码沙箱和@Taplar 评论清楚地说明了......谢谢
  • @Philip 我很好奇为什么 this 没有为你定义。我做了一些阅读并试图澄清我的答案。这应该会有所帮助。
猜你喜欢
  • 2018-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
  • 2019-08-04
  • 1970-01-01
相关资源
最近更新 更多