The terms "initialization" and "assignment" seem to be used interchangeably. I did some searching and it seems that there might technically be a difference. My understanding is that, in the context of variables, a variable is initialized when the JavaScript engine makes it available for use, and assignment (whether done explicitly [as in let foo = 1;] or by the JavaScript engine, as in the following example) is one way to achieve this.
let foo;
console.log(foo); // undefined (initialization and assignment?)
Is my understanding correct? Also (if so), what actually occurs during initialization to make the variable available?
TLDR:
{ // declaration (hoisted)
// Temporal deadzone
let foo; // declaration and initialization to undefined
foo = 1; // assignment
}
A bit longer:
Declaration
Declaring a variable means that we reserve the identifier at the current scope. In javascript declarations are hoisted, that means that it gets declared when the scope the variable is in gets visible (the block it is in gets executed). However you cannot access that variable now as it is in
The temporal deadzone
This is a specific part of the code that is between the beginning of the scope and the initialization. Trying to access the variable here results in an error.
Initialization
The initialization takes place in the line were you declared the variable. It will assign a value to the variable and will make it available for access. This for example:
let foo;
will initialize foo to undefined,
let foo = 2;
will initialize foo to 2.
Assignment
...just means that you change the value of a variable. All assignments in javascript use =. The initialization is basically just the first assinment.
The explanation above does not apply to variables declared with var, so just don't use var to avoid confusion :)
Related
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...)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Difference between using var and not using var in JavaScript
I understand that I should always use 'var' to define a local variable in a function.
When I define a global function, what's the difference between using 'var' ?
Some of code examples I see over the internet use
var globalVar = something;
globalVar = something;
What's the difference?
Well, the difference is that technically, a simple assignment as globalVar = 'something'; doesn't declare a variable, that assignment will just create a property on the global object, and the fact that the global object is the last object in the scope chain, makes it resolvable.
Another difference is the way the binding is made, the variables are bound as "non-deletable" properties of its environment record, for example:
var global1 = 'foo';
delete this.global1; // false
global2 = 'bar';
delete this.global2; // true
I would strongly encourage you to always use the var statement, your code for example will break under ECMAScript 5 Strict Mode, assignments to undeclared identifiers are disallowed to avoid implicit globals.
Short: There is no difference in the global context**.
Long: The Variable object for the global context is the global context itself. That is why we can just access global variables and methods within a function-, or eval context. However, the var statement just makes sure you're defining a variable in the current context, so it makes no difference by omitting that in the global context. Exception: ES5 strict mode will probably throw an error when it sees a declaration without var.
**
the only difference is, that var will declare a variable without definition in the current (execution) context. This happens at js parse time, so the engine knows that there is a variable with that name available in the context. Omitting var will end up in a direct property access from the global object.
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.
See this code:
<script>
let {foo} = null; // TypeError
</script>
<script>
// Here I want to assign some some value to foo
</script>
The first script attempts to let-declare foo via a destructuring assignment. However, null can't be destructured, so the assignment throws a TypeError.
The problem is that then the foo variable is declared but uninitialized, so if in the 2nd script I attempt to reference foo, it throws:
foo = 123; // ReferenceError: can't access lexical declaration `foo' before initialization
And let variables can't be redeclared:
let foo = 123; // SyntaxError: redeclaration of let foo
Is there any way to take it out of the TDZ, so that I can assign values and read them?
It's impossible. Temporal dead zone and restricted access to uninitialized let variable are expected to be unavoidaible. It's confusing and problematic, but intended and expected.
See spec for details:
NOTE let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.\
So if variable wasn't initialized on declaration (and throwing before initialization obviously result in no initialization) it can't be accessed by any means.
But in fact, your issue is more complex than throwing assigment. It's architecture issue - you are depending on mutable global variables. It's big "no no no" and you should refactor your code to use explicit dependencies.
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.