We have something like this
if(true) {
const a = 1;
function myFunc() {
alert(a);
}
myFunc();
}
In Safari 11 this cause "ReferenceError: Can't find variable: a"
.
The same code works without error in Chrome and Firefox.
Using "strict mode"
in Safari solve the issue.
I think that the main problem is the different scope of const a
and function myFunc
. The last one, in fact, is a global function due the fact that the conditional statement does not create a block scope for functions inside it (I suppose for legacy reasons) as it does for let and const.
I'm wondering if Safari has right in this case because we're mixing things with different scope.
Is there some official resource that explains this case? I don't find any mention of this behaviour either in caniuse and mdn sites
Function declarations inside blocks weren't defined in the specification for many years but they were allowed by different javascript engines.
Since this syntax was not defined in the specification and was allowed by the javascript engines, different engines did different things. Some made it a syntax error, others treated function declarations in block scopes as they were function expressions. Some engines treated functions declarations in a block scope like multiple hoisted declarations in the same scope.
As of ES2015, function declarations are part of the specification and there are two ways they are handled:
With standard semantics, function declarations are converted to function expressions, declared with let
keyword and are hoisted at the top of the block. Standard semantics are in effect in strict mode.
So in strict mode, your code will be treated by the javascript engine as though it were written like this:
if(true) {
let myFunc = function() {
alert(a);
}
const a = 1;
myFunc();
}
In non-strict mode on browsers, legacy web semantics apply. When function declarations in block scope are not treated as syntax errors, there are three scenarios that are handled the same way by all major javascript engines. Those three scenarios are:
In addition to let
variable for the function defined in block scope, there's also a variable defined with var
in the containing function scope or the global scope. This var
assignment isn't hoisted to the top of the block and is done when the function declaration is reached in the code.
Your code in non-strict mode is treated by javascript engine as:
var varMyFunc;
if(true) {
let myFunc = function() {
alert(a);
}
const a = 1;
varMyFunc = myFunc; // at the place of function declaration
myFunc();
}
You shouldn't write code that relies on legacy web semantics. Instead, use strict mode to ensure that your code relies on standard rules for handling function declarations in block scopes. Having said all that, if you have legacy code in non-strict mode that relies on legacy web semantics, you can expect it to work cross-browser.