It appears JS strict mode disallows declaring of implicit globals, but does not affect access of variables defined inside a conditional (if) or loop (while). In C#, you cannot access a variable declared within a conditional or loop externally. In JavaScript, however, the following works:
"use strict";
(function() {
var mybool = true;
if (mybool) {
var test = "test;
console.log(test); //"test"
}
console.log(test); //"test"
}());
But it breaks in JSBin and jsFiddle, saying that test1 is out of scope.
Why does it work on my webserver, but breaks elsewhere?. What are the rules on scoping within conditionals and loops and such... is JavaScript supposed to be like C# in this way?
I think you're getting strict mode and JSHint errors confused.
In JSBin, JSHint is issuing the 'test1' used out of scope error. This has nothing to do with strict mode. On your webserver, everything works correctly because strict mode has no opinions on this kind of variable scope issue. JSBin is running JSHint for you, and your web server is probably not.
What are the rules on scoping within conditionals and loops and such... is JavaScript supposed to be like C# in this way?
No. JavaScript has no block scope, but it does have function scope. This means that if a variable is declared inside of a block (like an if statement or loop construct), its declaration is "hoisted" to the top of the function. Check out this great article for more on variable declaration hoisting.
For your code in particular, it's interpreted just as if you've written it like this:
(function() {
var mybool = true,
test; /* = undefined */
if (mybool) {
test = "test;
console.log(test); //"test"
}
console.log(test); //"test"
}());
To avoid this kind of confusion over scope in JavaScript, it's a good practice to declare all of your variables at the top of the function they're in (since that's where they're actually scoped to anyway).
Related
I ran into a issue eventually it turned out because I did not use var prefix during declare a local variable within a function, so when it get to next function it actually automatically pick it up the variable, even though that was a syntax mistake as my initial intent is to use another local variable with similar name. So is there a setting on google apps script to throw error on a global variable declaration within function to avoid this kind of tricky issues?
Here is sample code of my problematic issue
function f1(){
for(var idx=0;idx<length; idx++){
tmp1 = idx; // since tmp1 missing var, it endup as global variable
...
}
}
function f2(){
Logger.log(tmp1);// even though I did not delcare tmp1 here, it will not throw either validation nor runtime error.
}
Use the JavaScript strict mode. To do this, add the following at the global scope:
'use strict';
The above string literal could be added on any file in your Google Apps Script project, just be sure to add it outside of any function. In order to make this easier to find, add it on top of your project's first file.
Resources
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
Related
Enforcing strict mode in google-apps-script files using Chrome on Chromebook
From MDN article about strict mode:
First, strict mode makes it impossible to accidentally create global
variables. In normal JavaScript mistyping a variable in an assignment
creates a new property on the global object and continues to "work"
(although future failure is possible: likely, in modern JavaScript).
Assignments, which would accidentally create global variables, instead
throw an error in strict mode:
'use strict';
// Assuming a global variable mistypedVariable exists
mistypeVariable = 17; // this line throws a ReferenceError due to the
// misspelling of variable
What does this mean? Does the engine detect if a variable with a similar name already exists(?!) or does strict mode merely prohibit declaring global variables outside of global scope?
The above quotation seems to hint the first possibility but this seems... weird?
The comment in the quoted code is misleading: The "Assuming" part is irrelevant. It should really read: "Assuming that no global variable with exactly this name was defined with let or var,..."
But do note what the purpose was of the code snippet and the comment: to show that in strict mode it is much easier to spot spelling mistakes. In sloppy mode the spelling mistake would go unnoticed (no errors) and you would be working with two variables without knowing it (at least for a while)
As requested, I'll make this into an answer :)
I think you understand what happens here already, and are just getting confused through reading that quote about "assuming a global variable..." too literally. (I will admit it is phrased in such a way as to possibly lead to this confusion though.) The real situation is very simple, and has nothing to do with variables having "similar names" (which JS has no concept of):
What is being talked about is what happens if you assign a value to a variable which has not been formally declared (variables are declared with one of the keywords var, let or const). Not declaring your variables is bad practice, and in strict mode will throw an error - this is a good thing and warns you of your mistake. But in non-strict mode, JS will happily accept this and think you wanted to declare a global variable of that name. This is almost never what you actually want, as it pollutes the global namespace, doesn't inform you of your error, and may lead to all sorts of tricky bugs later.
does strict mode merely prohibit declaring global variables outside of global scope?
strict mode definitely doesn't prohibit declaring global variables from anywhere. In non-strict mode, if you write:
someVar = 't';
it will evaluate to:
window.someVar = 't';
( why is this happening? ) despite of writing inside or outside of a function scope. actually, the line was both declaring and evaluation of variable ( look again, it doesn't have var so it shouldn't declare anything! ).
But it would cause such a side-effect that wasn't totally fine and they've introduced strict-mode which when is active, our first line will throw an error. because it's just evaluation without declaring it first.
now if you need to manipulate global scope inside of a function scope, you only should global object as reference:
var someGlobalVar;
var someOtherGlobalVar;
function hello() {
// this will make *someGlobalVar* to be redefined in the inner scope
// someGlobalVar != window.someGlobalVar
var someGlobalVar;
// manipulating inner variable, not global one
someGlobalVar = 's';
// it's fine, you've accessed global object correctly
window.someGlobalVar = 's';
// you're still editing global object, because
// there's no other variable inside of this scope having the same name
someOtherGlobalVar = 's';
}
I am just learning JavaScript, and I don't understand why the following code doesn't produce an error:
myTest = 5;
function addFifteen(num) {
return num+15;
}
document.write(addFifteen(myTest));
Why do I not need "var" before "myTest"? If it runs without "var", what is the purpose of writing that?
When you do not specify a var before the variable, it is still valid javascript. This is why it does not produce an error. However, as a best practice, you should avoid this, because variables thus declared get tagged to the global scope window.
Having too many variables / functions thus declared is said to "pollute" your global scope and isn't considered good programming practice.
There is a more thorough explanation about this on MDN
This code works alerts "ok" in all browsers except Chrome:
eval("var outer = 0; function test() {'use strict'; outer = 1; } test(); alert('ok');");
(Try it on jsfiddle).
All I'm doing is referencing an outer variable from a 'use strict' function, all in eval context. Chrome says
Uncaught ReferenceError: outer is not defined
Note: I originally faced it when using devtool: 'eval' in Webpack.
To simplify the problem : http://jsfiddle.net/rokkkjcs/6/
eval("var outer=0;");
function test() {'use strict'; outer = 1; }
test();
alert('ok');
And the explanation is :
Non-strict code can use the ‘eval’ function to add new variables to the surrounding scope. Prior to native JSON support in browsers, ‘eval’ was commonly (and unsafely) used to construct objects from strings. The constructed objects then became a part of the surrounding scope. In strict mode, ‘eval’ cannot introduce new variables. When executed in strict mode, the following piece of code will not introduce the ‘bar’ variable into the surrounding scope. Note: if a function containing ‘eval’ is executed in strict mode, then the code inside the ‘eval’ function is also executed in strict mode.
More info : http://cjihrig.com/blog/javascripts-strict-mode-and-why-you-should-use-it/
Actually eval creates variables or modifies variables in scope where it is defined, no matter if you use val or not. Or in other words, by default it doesn't have it's own scope.
So when you do this
eval("var outer = 0;");
console.log(outer); //0
you create variable in outer scope. Surprisingly, this works the same way in chrome too - and it doesn't matter if window.onload is used or not.
To make eval has it's own scope you must do the following:
eval("'use strict'; var outer = 0;");
console.log(outer); //Uncaught ReferenceError: outer is not defined
Now there is a separate scope for eval.
With 'use strict' in eval your code will work in chrome and won't allow to override variables outside eval.
eval("'use strict'; var outer = 0; function test() {'use strict'; outer = 1; } test(); alert('ok');");
So this part answers how can you avoid error.
The second part that I am very intersted in but couldn't find an answer by myself.
The question is following, why your code throws an error in chrome while this works in chrome(which means the global variable is created):
window.onload = function() {
eval("var outer = 0; function test(){console.log(outer); } test();");
}
And also why it happens only with window.onload.
I submitted my addon to the AMO direcotry and the editor came back with this:
There are still a number of variables being leaked to the global scope,
probably because you're using them undeclared like...
He did not mention all the problem variables, is there anyway to know which are in global scope / getting leaked?
I have a crapload of variables and it would take ages going through each one of them to make sure they were declared properly with a "var".
Please help!
Thanks!
If you're trying to track down variables that may have been implicitly declared as global because of the omission of var, you could run the code in strict mode. This will give you a ReferenceError if you try to use variables that haven't been property declared.
(function() {
"use strict"; // <-- this runs code inside this function in strict mode
// your code...
test = 'tester'; // gives a ReferenceError
})();
You'll need to run it in a supported browser, like Firefox 4 or higher. The "use strict"; declarative will ensure that any code inside the function will be evaluated using the rules of strict mode.
Besides properly using the var keyword, you should make sure all your javascript is wrappend in a function like this:
(function(){
//Your code
}());
This keeps all your variables within the scope of an immediately invoked function.
Use firefox with firebug, add a break point somewhere appropriate and watch the "window" object, all the variables within the global scope are a member of it.