-1

I'm having this problem and I can't find any solutions maybe you guys can help?

Why can't I access this._activeScene? it always returns undefined even though I already set a value for this._activeScene

class SceneManager {
    constructor() {
        this._activeScene = null;
    }

    init() {
        let loop = setInterval(function() {

            console.log(this._activeScene);
            // Returns undefined.

            if(this._activeScene != null) {
                const self = this._activeScene;
                self.options.update();
                self.options.render();
            }
        }, 16.66);
    }

    setScene(scene) {
        this._activeScene = scene;
        this._activeScene.options.initialize()
    }

    get activeScene() {return this._activeScene;}
}

let sceneManager = new SceneManager();
sceneManager.init();

let gameScene = new Scene();

sceneManager.setScene(gameScene);
Code Jabo
  • 3
  • 1
  • 6
  • 2
    POssible duplicate https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work – Jamiec Aug 19 '19 at 13:37
  • just use arrow function like ` init = () => {} ` – samad324 Aug 19 '19 at 13:38
  • Possible duplicate of [How does the "this" keyword work?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – Liam Aug 19 '19 at 13:40

2 Answers2

2

So this is a question of scope, when you create an anonymous function it's in the global scope E.G function(){}; not the scope of your class instance. This is one of the cases for using arrow functions () => {} because they run in the scope they are created in, however, arrow functions are an ES6 feature so if you need to do it without ES6 you can use the Function.prototype.bind method

ES6 Version

let loop = setInterval(()=>{ // using arrow functions

    console.log(this._activeScene);
    // Returns undefined.

    if(this._activeScene != null) {
        const self = this._activeScene;
        self.options.update();
        self.options.render();
    }
}, 16.66); // not sure you can have 16.66 milliseconds

Non ES6 using .bind

var loop = setInterval((function() {

    console.log(this._activeScene);
    // Returns undefined.

    if(this._activeScene != null) {
        const self = this._activeScene;
        self.options.update();
        self.options.render();
    }
}).bind(this), 16.66); 
 // using .bind create a pointer to the function in the scope of the given var 
 // so in this case we're putting the function in the scope of this 
 // not sure you can have 16.66 milliseconds
Barkermn01
  • 6,781
  • 33
  • 83
-1

this in this context is not the class instance. The problem here is scope.

The most actual correct solution would be to use arrow functions, which don't bind their own scope, but inherit it from the parent one, which in this case is the instance of SceneManager.

The code would look like this:

class SceneManager {
    constructor() {
        this._activeScene = null;
    }

    init() {
        // As suggested by Liam
        // ES6 arrow functions will automatically inherit the current scope
        // as their scope
        let loop = setInterval(() => {

            console.log(this._activeScene);
            // Will no longer output undefined, will output null instead

            if(this._activeScene != null) {
                const self = this._activeScene;
                self.options.update();
                self.options.render();
            }
        }, 16.66);
    }

    setScene(scene) {
        this._activeScene = scene;
        this._activeScene.options.initialize()
    }

    get activeScene() {return this._activeScene;}
}

let sceneManager = new SceneManager();
sceneManager.init();

let gameScene = new Scene();

sceneManager.setScene(gameScene);

Other "older" solution would be to use the let that = this; trick, which is a well known way to transfer the scope.

class SceneManager {
    constructor() {
        this._activeScene = null;
    }

    init() {
        let that = this;
        let loop = setInterval(function() {

            console.log(that._activeScene);
            // now outputs null

            if(that._activeScene != null) {
                const self = that._activeScene;
                self.options.update();
                self.options.render();
            }
        }, 16.66);
    }

    setScene(scene) {
        this._activeScene = scene;
        this._activeScene.options.initialize()
    }

    get activeScene() {return this._activeScene;}
}

let sceneManager = new SceneManager();
sceneManager.init();

let gameScene = new Scene();

sceneManager.setScene(gameScene);
Telmo Dias
  • 3,938
  • 2
  • 36
  • 48
  • 2
    You need to explain what you've changed and why or else we're all playing spot the difference – Liam Aug 19 '19 at 13:40
  • Accidentally I copied the text and not only the code. The solution is the let that = this; – Telmo Dias Aug 19 '19 at 13:40
  • You had a lot of typos – Hogan Aug 19 '19 at 13:40
  • 1
    a more up to date why of doing this, BTW, is to use arrow functions as they transfer scope into the function. I'll give you a plus one as your correct (I'm not sure this really warrants -2) though I still think this needs more explanation – Liam Aug 19 '19 at 13:42
  • 1
    This actually WORKED! I'm struggling with this for 3 days Thanks a lot! Have a nice day man. – Code Jabo Aug 19 '19 at 13:52