Reputation: 446
Why does it give an error?
!function() {
var a = 3;
setTimeout('console.log(a)');
}()
It works if to use a function instead of the string.
It also works with eval:
!function() {
var a = 3;
eval('console.log(a)');
}()
This question is about theory. No need to fix it.
The problem is with the scope. I don't understand why the scope is lost. Better - some sentences from a source (for example Mozilla site) where it is explained.
Upvotes: 1
Views: 170
Reputation: 40852
Because you can imagine the setTimeout
function as something like that
function setTimeout(callbackOrString, time) {
if( typeof callbackOrString === 'string' ) {
callbackOrString = eval(callbackOrString)
}
// pass callbackOrString to event queue
}
The evaluation of the string happens inside of the setTimeout
function, and not at the point when you pass it to the function. So at the point when the string is evaluated, there is no a
in that scope.
And eval
is not a regular function but more instruction for the engine, and its behaviour is defined here ECMAScript: 18.2.1 eval ( x )
setTimeout
is not part of the language specification but part of an API, and just a "regular" function Living Standard: timer initialization steps (see 1. and 3.):
1. Let method context proxy be method context if that is a WorkerGlobalScope object, or else the WindowProxy that corresponds to method context. … 7. Let task be a task that runs the following substeps: … 2. Run the appropriate set of steps from the following list: - If the first method argument is a Function … - Otherwise … 3. Let settings object be method context's environment settings object.
It is just as if you would write your own function, which results in the same error you get with setTimeout
for the exact same reason:
function myFunc(code) {
if (typeof code === 'string') {
eval(code)
} else if (typeof code === 'function') {
code();
}
}
!function() {
var a = 3;
// arrow function is passed to myFunc, closure is create so "a" is available
myFunc(() => console.log(a));
// function is passed to myFunc, closure is create so "a" is available
myFunc(function() {
console.log(a)
});
// the string "() => console.log(a)" is evaluated in the scope of "a" and the resulting arrow function is passed to myFunc, closure is create so "a" is available
myFunc(eval('() => console.log(a)'));
try {
// does not work because only the string is passed to myFunc which is evaluated within myFunc
myFunc('console.log(a)');
} catch (err) {
console.error(err);
}
}()
Upvotes: 2