I am going through the book JavaScript: The Complete Reference, Third Edition By: Thomas Powell; Fritz Schneider to have a detailed understanding of the concepts.
Scoping Rules
Outside of a function or object, variables are within the global space whether explicitly defined with var or not. Within a function or object, if the var statement is used, the defined variable will be local to the construct; without the statement, it will be global.
Commonly, JavaScript developers make assumptions about scoping rules with var that aren’t quite true. For example, a var statement found within a for loop does not scope that value to the loop. In this case, it is scoped to either the function it is within or to the global space if it is outside a function or an object.
Just to see what happens consequently, I coded like this,
When I press Ctrl+Space in Eclipse IDE for it to show JavaScript proposals, why am I able to access the variable jLocal outside the function?
As per the author description:
For example, a var statement found within a for loop does not scope that value to the loop. In this case, it is scoped to either the function it is within or to the global space if it is outside a function or an object.
Because at the bottom of your code you have:
...
jLocal = jLocal + j; // defined not in any functions
...
Making it global, but not necessary defined.
It isn't the case of a local function. myFunc is global, just as the variable jLocal is (albeit the name). Because of hoisting, jLocal is assumed to be declared on top of parent scope.
Looking more carefully, there's two variable's named jLocal. One local to myFunc and an implicit one on global scope.
Want a tip?
Put "use strict"; just before var global1 = true;. An HTML 5 implementation would be able to catch and show your error.
Related
When I run the following code written using ES6 let:
function doSmth(){
age=27;
}
let age;
doSmth();
console.log(age);
I get the output correctly as 27.
Now I am a bit confused as to how this works. Specifically, I have read is that let has a block scope.
So, how is it that let age, which is declared outside the function doSmth, is the same variable inside the function doSmth?
Is that something to do with global scope, but if yes, then what role does block scope have exactly ?
"Let has block scope" doesn't mean that the variable must be defined in the same block - it means that the level at which the variable is defined can be a block.
In contrast, the level at which a var variable is defined cannot be a block, unless that block is also a function block. If a var variable is initialized inside a non-function block, it will be hoisted out to the nearest outer block which is also a function block.
No matter whether a variable is declared with var, let, or const, it will be referencable anywhere else inside the same block in which it was declared, including inside child blocks. (For let and const, the line that declares the variable, eg const foo =, must also have run for a reference to foo to work without throwing.)
Another way of looking at it is: if a variable is referenced somewhere, the interpreter looks for whether it's initialized in the current block. If so, that's the binding that's used. Otherwise, it looks in the next outer block to see if the variable is initialized there - if so, that's the binding that's used. And so on. If it gets all the way to the top level, and no binding has been found, and it's not a property of the global object, that reference will throw a ReferenceError.
In your code, the age variable happens to be global, since it's defined on the top level, but that doesn't really matter - the code would work the same if age was just an an outer block, but not on the top level, eg:
function foo() {
function doSmth() {
age = 27;
}
let age;
doSmth();
console.log(age);
}
foo();
Now I am a bit confused as to how this works. Specifically, I have read is that let has a block scope.
Yes, let has block scope. But a block such as inside your doSmmth() function has access to ALL the variables in parent scopes that have been defined or hoisted at the time of the function execution.
So, let age; is in the parent scope and has been defined by the time doSmth() executes. Therefore the interpreter tasked with looking up a variable named age finds it in the parent scope just fine.
So, how is it that let age, which is declared outside the function doSmth, is the same variable inside the function doSmth?
Here's how the interpreter works in this regard. Your code executes in these steps:
Parse the code you've presented.
The function doSmth() definition is encountered. Functions are hoisted to the top of the containing function scope so that definition is immediately added to this scope object. let age is also encountered in the parsing and is added to the parsed scope, but it is marked in a way that it will not yet be accessible to code.
Start executing the code. A new scope object is created from the parsed code.
let age; is encountered. This marks the age variable which was previous put into this scope at parsing time as now available for code to use.
Call doSmth(). This pushes a return address on the callstack and as it sets up to execute doSmth(), it creates a new function scope.
It then executes age = 27 inside that function. The interpreter looks in the local scope for a symbol named age. It doesn't find one. So, it goes up the scope chain and looks in the parent scope. It finds a matching symbol and uses that one.
The function returns which pops a return address off the call stack and resets the current scope back to the parent scope and the execution goes to right after where doSmth() was and executes the console.log(age).
The interpreter looks in the local scope for a symbol named age and finds it and uses it.
So, the main keys here that it sounds like you may be confused by are:
If a symbol isn't found in the local scope, then the parent scopes are searched for the symbol name.
let does create a block-scoped symbol, but it can still be accessed by any child functions also declared in that scope.
In this particular code, there wouldn't be any difference between let and var and it isn't hoisting that makes this work. It's parent scopes.
In your code, both doSmth and age are in global scope. Windows for browser and module object for node. So they basically are in the same scope.
Your code will go through two phases.
Compilation where all hoisting will occur (var declared variables).
Execution where actual execution of code will occur.
In first phase you will declare function doSmith and age variable. Before executeion phase JS engine knows what is age variable and what is doSmith.
In next phase i.e. execution phase, execution will occur,
doSmth();
console.log(age);
will get executed. Hence in age variable is accessible to function doSmith in execution phase.
Looking at MDN's introduction to JavaScript, Grammar and Types section - one reads:
Declaring variables
You can declare a variable in three ways:
With the keyword var. For example, var x = 42. This syntax can be used to declare both local and global variables.
By simply assigning it a value. For example, x = 42. This always declares a global variable. It generates a strict JavaScript
warning. You shouldn't use this variant.
With the keyword let. For example, let y = 13. This syntax can be used to declare a block scope local variable. See Variable scope
below.
The following code snippet would seem to fit the "by simply assigning it a value" scenario, meaning the variable should be treated as global.
(function(){
console.log(myVar);
//the following will throw a ReferenceException error
//myVar = 10;
//the following will not, and I can understand it following the defintion of the behavior of using `var` keyword
//var myVar = 10;
})();
But running the code will generate a ReferenceException when myVar is commented, and undefined when not. I would expect it to generate undefined in both cases, since if myVar is a global variable (per definition), than javascript's variable hoisting would make it known before reaching console.log(myVar);
What is the explanation behind such behavior ? (the behavior I described is what I get when trying it in my firefox's console, but running it in jsfiddle will not throw an error).
Are self-executing functions an exception to hoisting ?
the "by simply assigning it a value" scenario
You are reading the value, not assigning it
if myVar is a global variable (per definition),
It isn't.
myVar is:
a variable scoped to the function if the function contains var myVar (or function myVar () { ... }, or it is listed as a parameter in the function definition).
a variable scoped to the block if the block contains let myVar
a global variable if a value has been assigned to it previously and neither of the above conditions are true.
Since you haven't assigned a value, it isn't a global. Since none of the above conditions are true, it isn't any kind of variable, so you get a reference error.
Regarding your comment:
I left my var when I meant var in the scenario I am trying to depict. Updated question.
… and the edit to which you refer:
Commented out code is not evaluated. Having a comment that uses the keyword var doesn't do anything.
Regarding your further edits.
You get a reference error if you try to read a variable before it has been declared.
var statements (and function declarations) are hoisted so a variable declared with those methods can be read anywhere in the function.
Assignments are not hoisted. A global variable created implicitly by assignment (which is generally not considered to be best practise and is banned in strict mode) can't be read before the value is assigned.
Since my comment seemed to help explain it to you, I will turn it into an answer:
Implicit global variable creation (when you don't actually declare it, but just assign to it) is NOT hoisted. The variable creation happens inline at the moment the assignment occurs.
Thus, when you try to read the variable, it does not exist yet and that is an error.
var or let declarations are hoisted to the top of their appropriate scope.
All of this should hopefully help explain why you should just run in strict mode where implicit global creation is illegal and not allowed and triggers an immediate error. It's basically evil. A simple typo misspelling a variable may not trigger an error when you really want it to.
I'm reading about function declarations and function expressions. It say's that function declarations get placed in the initialization stage of JavaScript. Later I found out that variables also get placed in there, but they get the value of undefined, and when the interpreter reaches that variable it will then be assigned.
I can't find a good article about it. Now I'm wondering what else gets in the initialization stage of JavaScript?
Try searching for javascript hoisting (W3Schools).
It basically means, no matter where you declare a function or a variable, when the code is run the functions and variables are hoisted to the top and declared. This is why you can declare functions after you've used them and they still run fine.
However, initializing them is different. From W3Schools
JavaScript only hoists declarations, not initializations.
This means that the variables only get their assigned value when they reach the relevant line; functions are the same.
If you use a function expression such as:
var foo = function() {
// your code
}
In this case only the variable name will be hoisted and given the value undefined, and then if the function gets called above the function expression it will error as it has not have been assigned the function at this point in the code.
I was looking at adding comments to JSON and found this script that strips them out before processing making the JSON valid. I am just trying to understand how it works to make the JSON.minify() function available?
It starts with
(function(global){ ...
totally which is weird to me. I found that "global is a property of a RegExp instance, not the RegExp object" on MDN but I don't understand how it is works in this script if at all.
This snippet:
(function(global){
// your code here
// referring to the variable named "global" in this scope
// will be a reference to the default javascript global object
})(this);
is a construct for assigning the global object (whatever it might be) to an argument labeled global for all code that is inside this self-executing function.
The self executing function is used to define a separate execution scope so that any functions or variables you define inside this other scope will not interfere with or be directly accessible from outside this scope (insulating your scope from other code scopes).
In a browser, the global object is the window object, but if you intended to have code that might work in other javascript environments (like no node.js on a server) where the global object might not be window, this is a way of extracting the global value from the default this value, putting it into another variable which you can then refer to anywhere inside your code block.
For code mean to only run in a browser, there really is no point to this. You can just refer to window when you need the global object.
It's just a function parameter name. It might as well be froozboggles.
This code:
(function(foo) {
// In here, what's called "bar" in the outer scope is called "foo"
})(bar);
Defines an anonymous function taking one parameter bar and immediately calls it with the value of bar as the first parameter.
Apart from what jfriend00 mentions in his fine answer, it's also a good way of making sure that you don't leak variables and functions to the outer scope: If you declare, say, var baz = 17; in the top scope in javascript, it will be a property of window. If you wrap it in a function as in the pattern you mention, you can only export properties to window explicitly -- by assigning them to global, in the case of your example. Edit: As #josh3736 says in his comment, you can also leak to window by assigning without a previous declaration, e.g. quux = 4711;.
Just now,I saw some code like this:
if(condition){
var xx='sss';
}
//do something
if(condition){
console.info(xx);
}
Now, I just wonder why the second if statement work? How can it access the xx variable since it is a local variable defined in another if statement?
var in JavaScript is scoped to the containing execution context (e.g., the whole function's scope, or the whole global scope if the var is at global scope), not the block. JavaScript doesn't (yet) have block scope (ECMAScript6 looks likely to add it, via the new let keyword).
The code you've quoted is exactly equivalent to this:
var xx;
if(condition){
xx='sss';
}
//do something
if(condition){
console.info(xx);
}
This is covered by Section 10.5 of the specification, which describes what the engine does when entering a new execution context. (It's basically a two-phase process, first setting up all the declarations, and then executing step-by-step code.)
More: Poor misunderstood var
In JavaScript the scope is exacted to the closure (the last enclosing function block, or defaults to the window object). When a variable is declared anywhere within that function block it is hoisted to the top of the scope, so in essence a variable exists as undefined starting at the very top of the scope if it is declared anywhere in the scope.
Think of it like this, when the code begins executing it scans all the instructions for declarations and allocates the symbol name starting immediately.
console.log(x); // undefined
console.log(y); // error: Uncaught ReferenceError: y is not defined
var x;
for that matter you can take it to extremes:
console.log(x); // undefined, not an error
while (false) {
if (false) {
var x;
}
}
even though var x can't possibly be reached, and during execution would be optimized away completely. the engine will still hoist the variable to the top of the scope
hope this helps -ck
useful link: http://www.youtube.com/watch?v=taaEzHI9xyY&feature=youtu.be#t=42m57s
var declarations affect the entire scope of the smallest containing function or program. JavaScript is not block scoped.
Crock says:
Variable Declarations
All variables should be declared before used. JavaScript does not require this, but doing so makes the program easier to read and makes it easier to detect undeclared variables that may become implied globals. Implied global variables should never be used.
The var statements should be the first statements in the function body.
It is preferred that each variable be given its own line and comment. They should be listed in alphabetical order.
var currentEntry; // currently selected table entry
var level; // indentation level
var size; // size of table
JavaScript does not have block scope, so defining variables in blocks can confuse programmers who are experienced with other C family languages. Define all variables at the top of the function.
Use of global variables should be minimized. Implied global variables should never be used.
Note, this is changing with the let statement, and in current JavaScript (EcmaScript 5), the variable name in a catch block is block scoped.
javascript doesn't have block scope, so var xx='sss' is either locally scoped (if your sample code is inside a function) or globally scoped (if your sample code is not contained in a function).
JavaScript is a dynamic language, that isn't always picky about things like variable scoping. So, this "feature" allows you to write the code
if (condition) {
var xx = 'sss';
} else {
var xx = 'ttt';
}
// do something
if (condition) {
console.info(xx);
}
I would recommend avoiding this, since it makes your program harder to understanda and reason about.
If you declare a variable using var within a function, the variable is scoped to within that function. An if statement is not a function.
I'm assuming in this case both if statements are within the same function and therefore xx is in scope?
If a variable is declared inside a conditional statement, it is still available anywhere following the declaration in the containing function (or globally if the conditional is not in a function). However, it will equal undefined if the condition evaluates to false, unless the variable is later assigned a value.
If a local variable is referenced globally or in another function, a JavaScript error will occur. Depending on your browser, the error may say the variable "is not defined" or "is undefined". This is different from the variable equaling undefined like mentioned above, because a variable that equals undefined can still be referenced.