简体   繁体   中英

This in class method vs arrow function

Good day

I recently ran into a strange situation where my this value in on a member function of a class is undefined. I know there are a lot of questions relating undefined this contexts, but I can't find any explanation of this issue. I would love to know why it happens. In this examples, why does the arrow function implementation of the setModified function keep its this context, but the function on the class does not.

This example breaks in the setModified function on the block class

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;

But when you change setModified function to an arrow function (also on the class), it works:

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;

Why would the member function on the class lose the this context but not the arrow function?

An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.

The scope of an arrow function is no different than a regular function, but as you can see above from MDN , they handle the binding of this different.

An arrow function does not create its own binding to this , in fact it inherits from the enclosing scope (which is the class). In the class, the this._modified exists.

An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules.

this is determined in the execution context, and as we've shown the arrow function works because this stays with the this of the enclosing scope. However with a normal function...

In strict mode, however, if the value of this is not set when entering an execution context, it remains as undefined

If you console.log(this) before the error in your function you will see it is undefined . But why is it undefined ? Read here under the implicitly lost section. But basically default binding is applied when calling it as a plain, undecorated function reference, which it is from the assignment. And under strict mode, this will return undefined while not in the global scope.

Implicit binding doesn't occur in your case and this can be implicitly lost.

One of the most common frustrations that this binding creates is when an implicitly bound function loses that binding, which usually means it falls back to the default binding, of either the global object or undefined, depending on strict mode.

If you change your call in runChangeEvent() to event.call(this) you will see that this is actually the point 's this (not what you want). If you call this._topLeft.changeEvent = this.setModified.bind(this); you will have the scope you want.

Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function, in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.

Check out that link for more information and also I recommend this online book referenced earlier.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM