0

This might be a vague question (or even answered before, if so just cast a close vote and I'm happy with the point in the right direction), and if I just read up on the different scopes of JavaScript a few more times I might be wiser, but seeing as I've done that over several years and still get perplexed by the way JavaScript scopes work - some of you might have a good/proper explanation of why the below snippet works in the way it does:

(I understand that there's scope/context involved, or maybe mutable variable types, but can't figure out the difference in the below code snippet and why it's illogical)

class test {
    constructor() {
        this.obj = document.createElement('div');
        let style = {'padding' : '5px'};

        this.obj.innerHTML = '<h3>Click the button:</h3>';
        for (let key in style) {
            this.obj.style[key] = style[key]; 
        }

        this.some_var = 55;
        this.update_state.call(this, 'test');
        this.update_state_working.call(this, 'test');
    }

    update_state(state) {
        if (state == 'test') {
            const frame = document.createElement('div');
            frame.innerHTML = '<input style="background-color: #00FF00;" type="submit" value="Not working" />';
            frame.onclick = function(event) {
                console.log('You pressed: ' + this.some_var);
            };
            this.obj.appendChild(frame);
        }
    }
}

This will fail, logging You pressed: undefined. Which to me coming from other language backgrounds, is a bit confusing.

update_state(state) {
    let test_var = this.some_var;
    if (state == 'test') {
        const frame = document.createElement('div');
        frame.innerHTML = '<input style="background-color: #00FF00;" type="submit" value="Working" />';
        frame.onclick = function(event) {
            console.log('You pressed: ' + test_var);
        };
        this.obj.appendChild(frame);
    }
}

While this on the other hand will work. I assume it's because test_var is a block scope locally defined in update_state and some how gets frozen in to the event function? But I don't get why the difference? The context in my mind would be the same since the event is bound to a certain DOM object or outer class scope/instance?

I found What is the scope of variables in JavaScript? which has a pretty good explanation of what the different scopes are in practical examples, and I thought that this.some_var would be accessible according to example #6. And this question, scope of variable inside an event listener callback function - hints that this.index should be accessible inside a onclick function, which to me - it isn't?

What is the best practice method in solving this context issue? Because I rarely see anyone solving it by doing let declarations, but maybe I've just missed something obvious?

I'm looking for an explanation (or a resource describing the pickle) to why this locally declared variable works, why the context of the test instance can't be used as a variable source like the link above suggested, and what the appropriate solution to this might be (if not declaring by block scope local variable).

JsFiddle: https://jsfiddle.net/2cbfhzt5/

Torxed
  • 22,866
  • 14
  • 82
  • 131
  • `this` is special, it doesn't follow the normal scope rules. – Barmar Jun 12 '18 at 17:40
  • @Barmar thank you for that reference, pretty good examples and a good explanation. I knew there was one out there some where. `.bind()` and the magical `() => ` appears to be good choices to solve the problem. Again, thank you! – Torxed Jun 12 '18 at 17:43

0 Answers0