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.
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.
As we know, traditionally JS has been lacking block scope. As we know JS has had only function scope up until recently.
In recent versions of JS though, we can have let and const variables which are visible only in the scope where they are defined.
But... deep down... how is this done/implemented really? Is there really now in the language a first-class notion of block scope in JS, or is the block scope thing just some simulation to make certain variables visible only in the block where they are defined?
I mean is block scope in recent JS versions a first-class thing, just like function scope is, or is block scope just some sort of simulation while we actually still have just the old good function scope?
But... deep down... how is this done/implemented really? Is there really now in the language a first-class notion of block scope in JS...?
Yes, there is. A new block¹ creates a new lexical environment in the same way that creating a function does (without, obviously, all the other aspects of creating a function). You can see that in the Evaluation section of blocks in the spec.
It's a first-class language construct.
¹ I originally wrote "...containing a let, const, or class declaration..." but the specification doesn't actually make that distinction, though I expect JavaScript engines do (since there's no need for a new environment if there are no lexically-declared bindings).
In a comment you've asked:
What about hoisting? I read that block scoped-variables are hoisted to the top of the block they are defined in... but then also... You get an error if you try to access a block-scoped variable before the line/statement where it is declared in its block? This sounds contradictory, no? If they are hoisted we would not be getting this error but we would be getting undefined. What is the truth here?
In my book I describe them as half-hoisted: The creation of the variable (more generally, the "binding") is hoisted to the top of the scope in which its declaration (let x or whatever) appears (the block, in this case), but the binding isn't initialized until the declaration is reached in the step-by-step execution of the code. The time between creation and initialization is called the Temporal Dead Zone. You can't use the binding (at all) within the TDZ.
This only applies to let, const, and class declarations. var is handled differently in two ways: 1. Obviously, var is hoisted to the top of the function (or global) scope, not just block scope. 2. Less obviously, var bindings are both created and initialized (with the value undefined) upon entry to the scope. They're fully hoisted. (The declaration of them is; any initializer on the var statement is actually an assignment, and done when that statement is reached in the step-by-step execution of the code.)
Here's an example:
function foo(n) {
// `l1` and `l2` don't exist here at all here
// `v` is created and initialized with `undefined` here, so we can happily
// do this:
console.log(`v = ${v}`);
if (n > 10) {
// `l1` and `l2` are created here but not initialized; if we tried to
// use them here, we'd get an error; uncomment this line to see it:
// console.log(`l1 = ${l1}`);
console.log("n is > 10");
var v = "a"; // `v` is assigned the value `"a"` here, replacing the
// value `undefined`
let l1 = "b"; // `l1` is initialized with the value `"b"` here
console.log(`l1 = ${l1}`);
let l2; // `l2` is initialized with the value `undefined `here
console.log(`l2 = ${l2}`);
l2 = "c"; // `l2` is assigned the value `"c"` here, replacing the
// value `undefined`
console.log(`l2 = ${l2}`);
}
}
foo(20);
Just for completeness, function declarations are also fully-hoisted, but even more so than var: The function is actually created and assigned to the binding upon entry to the scope (unlike var, which gets the value undefined).
In a comment you've observed:
Then... I don't see what's the difference between no hoisting and half-hoisting...
Good point, I didn't explain that. The difference relates to shadowing identifiers in the outer scope. Consider:
function foo() {
let a = 1;
if (/*...*/) {
console.log(`a = ${a}`);
let a = 2;
// ...
}
}
What should the log show?
Sorry, that was a trick question; the log doesn't show anything, because you get an error trying to use a there, because the inner a declaration shadows (hides) the outer a declaration, but the inner a isn't initialized yet, so you can't use it yet. It's in the TDZ.
It would have been possible to make the outer a accessible there, or to make the inner a accessible there with the value undefined (e.g., fully hoisting it like var, but just within the block), but both of those have problems the TDZ helps solve. (Specifically: Using the outer a would have been confusing for programmers [a means one thing at the beginning of the block but something else later?!] and would have meant JavaScript engines had to create new lexical environments all over the place, basically every let or const or class would introduce a new one. And pre-initializing with undefined is confusing for programmers, as var has shown us over the years...)
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 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.
I have a Sublimelinter installed in Sublime Text 2 and it's great. However it doesn't like the following code:
if(condition){
var result = 1;
}else{
var result = 2;
}
process(result);
It says for var result = 2; that result is already defined and for process(result); that it's used out of scope. Is it just mistaking the {} of the if statement for a more closed scope or should I really be doing it like this:
var result;
if(condition){
result = 1;
}else{
result = 2;
}
process(result);
No it is not "wrong"; it will get hoisted to the top of the nearest function definition, as per the ECMAScript specification.
Yes, your program "Sublimelinter" is incorrect to claim the variable is out of scope.
It is not wrong. If you get that error, you defined result earlier in your code.
You can also simplify your condition to this so you don't have to use result:
process( condition ? 1 : 2 );
Javascript doesn't have 'block-scoping' like many other languages. If it did, the variable result would not exist when you tried to call process(result) because it would not be possible to reference it outside of the {} block where it was defined.
However, javascript only has function scoping, where variables in one function cannot be accessed by another function. Where variables are declared in the function has no significance whatsoever, because it will still be accessible from anywhere inside that function (no block scope). Hence, both code snippets you posted are equivalence to whatever interpreter is running the code.
The second is preferable because it is clearer as it shows where the variable will be used (throughout the function). It also prevents the variable from being declared twice inside the scope of the function, which may not necessarily cause anything bad to happen, but it will definitely not cause anything beneficial. You should almost always declare a variable once at a higher level instead of twice at different lower levels, even though it doesn't really matter since the scope of the variable will be the entire function no matter where it is declared.
JavaScript does not have block scope. Variables are scoped to the function they are defined in, meaning that when you declare a variable inside of an if block, it is "hoisted" to the top of the function.
Since the variable is technically defined at the top of the function anyway, it is considered a best practice to move variable declarations to the top of the function so that the intent of the code is clear.
I'd say your code isn't "wrong" but it is misleading to people reading the code who aren't familiar with how scope works in JavaScript. I would definitely opt for the second version, because it actually reflects how the code is executed.
Here's a good article explaining variable hoisting.
Declare your var pointer once with-in the function you have your if statement at. Like ninjagecko mentioned all vars get sent to the top of their containing functions.
However; do be careful because if you declare the same var twice like you have it , it will reset the var.
I recommend doing this:
var result = MORE_LIKELY_OUTCOME;
if (LESS_LIKELY_CONDITION) {
result = LESS_LIKELY_OUTCOME;
}
process(result);
This way, you are setting result initially to what you are expecting it to be most of the times.
Then, the if statement will change result if the condition occurs.
It turns out that SublimeLinter is using JSHint which has the option to surpress this warning and explains why it exists.
funcscope This option suppresses warnings about declaring variables
inside of control structures while accessing them later from the
outside. Even though JavaScript has only two real scopes—global and
function—such practice leads to confusion among people new to the
language and hard-to-debug bugs. This is way, by default, JSHint warns
about variables that are used outside of their intended scope.