-
Aaah - thanks for the brief explanation.
I was aware of the scope ofthis
and the technique of usingthat
but did not expect it to be an issue with just referencing a callback function likesetTimeout(this.log, …)
expecting it to be resolved just before the timeout is started.
However, thebind()
function was new to me, thanks for the suggestion.
And thatfunction() {}
is different from() => {}
regarding the scope ofthis
did not occur to me.
Javascript is easy and nice in some aspects, but has a lot of oddities when digging deeper...
@ChristianW,
sorry being off for a bit... did some woodworking... a totally different beast but great balance to the 'just sitting' and hacking.
To begin with the post #9* where some off the oddities (JavaScript: The Good Parts... Douglas Crockford): context of the 'this': For a generic understanding, JavaScript is - next to other many other things - a scripting language that supports functions and provides very low entry level in coding.... and that is what happens with the setTimeout() and setInterval(): the first argument is a function(), nothing else... and when it is 'finally' called, the function knows nothing about the very high level concept of coding nor oo - context - (anymore) when it was passed as argument to the setTimeout() and setInterval(). The execution hits the 'this' and tries to resolve it... and it may get a context, just not the one you intended.
Recall of code from post #9:
This - original context not known anymore - is not all bad, because:
But you figure... and you figured it - almost - because another oddity made it right: fat arrow functions do not have their own context like a regular function... oops... you got mocked by JS's understanding of context... again... The fat arrow function has no own context like a regular function has but takes the creation context and holds on to it...
Therefore, to overcome the 'this' context for setTimeout() and setInterval() with regular functions, JavaScript offers multiple options:
The first, always working one, is to bind the function to the context you want before passing:
The bind() in line #4 makes sure the any this in function passed refers to the context (and object) you intended.
Alternative and not to use the bind(), you set a local variable to point to the context (this) and used that in the function (some use 'that' for that - word play). In the beginning I used _this, but most of the time I just use _ for the variable name and create an anonymous function (the bind() does this behind the scene... and is even more efficient):
You may notice some other things:
Latter two have nothing to do with what we discuss here. Have an onInit() to get the code activated at a 'conscious' point is a good practice. Last one makes sure that applicaiton code does not mess with the upload... because the upload is a running js code as well... (Espruino understands only JS thru the REPL in the console, and therefore, what is going on on upload is javascript execution. You can read about this in conversation about: simple explanation how to save code that espruino run on start?. I has aged... but is still true when uploading into the RAM. For simple examples it is never an issue, but if code is active - especially timeouts and intervals and dynamic inits, it is a bad practice to get them going before the upload is complete. @Gordon has done a lot to make the execution save and avoid contentions with the upload, but there is always the possibility that the application or a used driver to connect to a sensor or actuator has an initialization that cannot be know to the save() command nor to a (re)load and resume on power up / power cycle.
More is to be said about context... but this is enough for now.
Not having a clear - reproducible - initialization after power cycle / messing w/ the upload can cause some of the unpredictable things... like the ) found ( ...found ( Got ')' expected EOF ).