function test1(args){
let age = 23; // place debugger 1 here
var name = 'prajval';
}
test2();
function test2(...args){
let age = 23; // place debugger 2 here
var name = 'prajval';
}
test1();
scope at debugger 1:
Local
this: Window,
age: undefined,
args: undefined,
name: undefined
Global
scope at debugger 2:
Block
age: undefined,
name: undefined,
Local
this: Window,
args: []
Global
In test2() method when we used ...args as parameter that makes all other variable declarations of test2() method in block scope.
But I expected all of them (args and variable declaration) to be in the local scope itself just like test1().
Why this kind of different and unexpected behaviour is seen ??
JavaScript makes a distinction between complex parameter declarations and simple (ES5-compatible) parameter declarations. The former are creating an extra scope where only the parameters but not the variables declared in the body are visible. It does not make a difference for the rest parameter syntax in your example, but it does when there are default initialisers since those might contain closures (or worse, eval):
var x = 1;
(function(f = (() => x)) {
var x = 2;
console.log(f()); // Which variable "x" will f() return? Surprise!
})();
There's a consideration to drop this distinction and just always use two separate scopes - they would only be observable in the debugger anyway. However this might confuse people who expect that function(x) { var x; } has only a single x variable, and implementations would want to optimise away the unreachable binding anyway.
Related
I have been wondering for a while if I can use a variable in JS before it is defined,
such as the following:
var country = "USA";
switch (country) {
case "USA":
country = i;
case "blach":
//not finished yet
}
/*
put a whole
bunch more code here
*/
var i = 10;
Is this valid? Is it allowed? And if so, what is the technical term for it?
This is a technique used by the JavaScript engine called hoisting. The parser will read through the entire function before running it, and any variable declarations (i.e. using the var keyword) will be executed as if they were at the top of the containing scope. So your code behaves like:
var country;
var i;
country = "USA";
switch (country) {
case "USA":
country = i;
case "blach":
//not finished yet
}
i = 10;
So, i is declared throughout the entire scope, but its value is undefined until the i = 10 statement runs.
In ECMAScript terms, when a function is invoked, the function's new lexical scope builds its VariableEnvironment before any of the function's code runs. In ECMAScript 10.5, step 8:
8. For each VariableDeclaration... d in code, in source text order do
a. Let dn be the Identifier in d.
...
i. Call env’s CreateMutableBinding concrete method passing dn and configurableBindings as the arguments.
ii. Call env’s SetMutableBinding concrete method passing dn, undefined, and strict as the arguments.
This is quite a mouthful, but basically it says:
Before you run a function, look through the function's source code for declarations like var [identifierName].
For each declaration you find, create a new variable in the function's scope with the name [identifierName] used in the declaration and then set its value to undefined
It's called variable hoisting, and it's a good concept to understand as it can occasionally create bugs that are hard to track down.
For example:
var stuff = 'stuff';
function() {
console.log(stuff); //outputs 'undefined'
var stuff = 'other stuff';
console.log(stuff); //outputs 'other stuff'
}
The first console.log outputs undefined because the var stuff in the function was hoisted to the top of the function.
//theoretical compiled output
var stuff = 'stuff';
function() {
var stuff; //has not been defined
console.log(stuff);
stuff = 'other stuff'; //defined here
console.log(stuff);
}
Without knowledge of variable hoisting, this result may be confusing.
For this reason, if you look at professionally developed JavaScript code, typically you'll see all variables in a function are declared at the top.
Yes. In JavaScript, variables are hoisted
A variable statement declares variables that are created as defined in 10.5. Variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.ES5 §12.2
Where 10.5 step 8 is the part of interest
The guys answers are correct. for your example however it is worth noting that country is undefined. as aspillers has mentioned your code behaves as below
var country;
var i;
country = "USA";
switch (country) {
case "USA":
country = i;
case "blach":
//not finished yet
}
i = 10;
alert(country) //undefined;
but when your case statement ran for "USA", i was undefined so this was assigned to country. try it here in this fiddle.
I guess that you just need to be aware that although the variable declarations are hoisted, the value assignments aren't.
Yes, you can use a variable before declaring it. It's called hoisting in JavaScript.
One thing to keep in mind though is that only a variable's declaration is hoisted, not its initialization.
It means that you'll not able to use the value of the variable which you'll assign later in the code. For example,
console.log(myVar); //undefined
myVar = 10;
var myVar;
So you'll not get the error (myVar is not defined) even when you're using myVar before declaring it. But the console logged value of myVar will be undefined. At the starting of code execution, variable was declared, but its assignment was not. So the JS engine would see the above code block as
var myVar;
console.log(myVar); //undefined
myVar = 10;
var myVar;
It will put the declaration for all the variables used in the file at the top of the code.
I'm currently studying javascript by following the book "you dont know js" series.
In the "this & object prototype" section, when discussing "indirect references to functions", the author states
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
The result value of the assignment expression p.foo = o.foo is a
reference to just the underlying function object. As such, the
effective call-site is just foo(), not p.foo() or o.foo() as you might
expect. Per the rules above, the default binding rule applies.
So apparently, (p.foo = o.foo) return a reference to the function foo. But what is the mechanism/rules that allow (p.foo = o.foo) return a reference to the function foo? In other words, why a simple assignment return a reference to foo function?
When I want to understand something like this, I find it helpful to break it down step by step.
o.foo looks at the o object and finds a property named foo. It returns a reference that property, whatever it might be. In this case, the o.foo property is a reference to the function foo.
p.foo = o.foo takes the result from above (a reference to the function foo), creates a property in the p object which is also named foo. So now p.foo is also a reference to the foo function, exactly the same thing as o.foo.
That expression is wrapped in parentheses, so now you have whatever was on the left side of the = sign, or p.foo, which is (as a reminder) still a reference to the foo function.
Now we find the () at the end. This calls whatever function we have on hand at this moment. That is the foo function. Note in particular that we are not calling p.foo(). That would be a method call to the function that p.foo is a reference to, so inside that function, this would be set to p. But we're not doing that. We're just calling whatever function was returned by ( p.foo = o.foo ). As before, this is the same foo function, but we've now lost any connection it may have ever had to the o object or the p object.
So, when we make that call at the end, we are merely calling the foo function without setting this to any particular object. Because of that, when we make the call, this is set to undefined.
But we're not running in strict mode, so JavaScript "helpfully" doesn't want to give us an undefined this, so it sets this to the window object in a browser or the global object in Node.
Previously we did var a = 2;. So the window or global object actually now has a property named a, and the value of that property is 2.
So now when we do the console.log(this.a), we pick up the a property from the window or global object. That value is 2.
What if all this code was not running at the global level, but instead it was inside a function? What would happen then?
function test() {
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // was 2, but now is undefined
}
test();
Now when we call console.log( this.a ); inside foo, this still refers to the window or global object. But when we set var a = 2;, we aren't setting a global property any more. We're just creating a local variable. window.a or global.a is undefined (unless some other code previously set it).
Strict mode avoids some this weirdness. If we put a 'use strict'; at the top of the code, it will compile in strict mode. And now when we make that last function call at the end, where we're calling the foo function (again not as a method!), this is now set to undefined instead of window or global. Therefore, when we try to call console.log(this.a), the code fails because this is the same as undefined, and undefined does not (and couldn't) have an a property.
Let's try it:
'use strict';
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // was 2 in the original, but now throws an exception
The bottom line, at least for this particular example: always use strict mode! It is your friend.
The assignment operator = is an expression in JavaScript that produces (returns) the assigned value. Because it is an expression it can be used anywhere an expression is allowed, such as inside parenthesis.
For example:
let test = (a = b = c = { name: 'test' })
The code above would first evaluate the expression in the parenthesis and point the variables c, b, and a to the test object (in that order), then it would point test to the produced value from this expression. After that line is executed, a, b, c, and test will all point to the same object.
Similarly,
(p.foo = o.foo)
Would produce o.foo back (technically it would produce whatever o.foo is pointing to, which is the function foo).
As far as
(p.foo = o.foo)()
By adding the additional () after the parenths, we are telling the engine that we want to invoke whatever the expression (p.foo = o.foo) ends up producing. Thus we end up invoking the function foo. Similar patterns is used in IIFEs.
A helpful rewrite would be to think of the line above as doing this:
let produced = (p.foo = o.foo)
produced()
Further reading about statements vs expressions.
Consider three cases, where both a and k are undefined:
if (a) console.log(1); // ReferenceError
and
var a = k || "value"; // ReferenceError
seems reasonable, but...
var a = a || "value"; // "value"
Why doesn't the last case throw a ReferenceError? Isn't a being referenced before it's defined?
This is because of one of var's "features" called hoisting. Per the link:
Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code. (emphasis mine)
So, for example:
console.log(a);
var a = "foo";
Instead of throwing a ReferenceError as you might expect, since a is referenced before it is defined, it logs undefined. This is because, as mentioned earlier, the declaration is processed first and essentially happens at the top, which means it's the same as:
var a;
console.log(a);
a = "foo";
The same goes for functions as mentioned earlier:
function foo() {
console.log(a);
var a = "foo";
}
That's the same as:
function foo() {
var a;
console.log(a);
a = "foo";
}
To see why, look into the ECMAScript 2015 Language Specification:
13.3.2 Variable Statement
NOTE
A var statement declares variables that are scoped to the running execution context’s VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created.
[...]
A variable defined by a VariableDeclaration with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the VariableDeclaration is executed, not when the variable is created. (emphasis mine)
From this, we can gather that the var declarations are created before any code is executed (in their lexical environment) and are scoped to the containing VariableEnvironment, which is either in the global scope, or in a function. They are initially given the value undefined. The next part explains that the value that the var is assigned to is the value of the right hand side when the declaration is executed, not when the variable is created.
This applies to your situation because in your example, a is referenced like it is in the example. Using the information earlier, your code:
var a = a || "value";
Can be rewritten as:
var a;
a = a || "value";
Remember that all declarations are processed before any code is executed. The JavaScript engine sees that there's a declaration, variable a and declares it at the top of the current function or global scope. It is then given the value undefined. Since undefined is falsey, a is assigned to value.
In contrast, your second example throws a ReferenceError:
var a = k || "value";
It can also be rewritten as:
var a;
a = k || "value";
Now you see the problem. Since k is never a declared anywhere, no variable with that identifier exists. That means, unlike with a in the first example, k is never declared and throws the ReferenceError because it is referenced before declaration.
But how do you explain var a = "123"; var a = a || "124"; // a = "123"?
From the ES2015 Language Specification again:
Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collective define only one variable.
To put it plainly: variables can be defined in the same function or global scope more than once, but always define the same variable. Thus, in your example:
var a = "123";
var a = a || "124";
It can be rewritten as:
var a;
a = "123";
a = a || "124";
Declaring a in the same function or global scope again collectively only declares it once. a is assigned to "123", then it is assigned to "123" again because "123" is truthy.
As of ES2015, you should, in my opinion, no longer use var. It has function scoping and can cause unexpected assignments like those mentioned in the question. Instead, if you still want mutability, try using let:
let a = a || "value";
This will throw a ReferenceError. Even though all variables are hoisted, no matter which declarator you use (var, let, or const), with let and const it is invalid to reference an uninitialized variable. Also, let and const have block scope, not function scope. This is more clear and normal regarding other languages:
function foo() {
{
var a = 3;
}
console.log(a); //logs 3
}
Versus:
function foo() {
{
let a = 3;
}
console.log(a); //ReferenceError
}
var a = k || 'value';
var a = a || 'value';
a is declared when you call var a but k is not, that why first line is ReferenceError and second line is 'value'
if (a) console.log(1); // ReferenceError
in this case i think there is no doubt as a is not declared anywhere before using it so refrence error
var a = k || "value"; //ReferenceError
same case here k is not declared and we are trying to use it
var a = a || "value"; //"value"
now in this case when we are trying use a (a in r.h.s) that time a is already declared before using it and hence no error
Adding to this if you want to know difference between reference error and undefined
refrence error -indicate variable is not declared yet whereas
undefined - it is special value in javascript assigned to any variable as soon as it is declared undefined indicates that variable is declared but has not taken any value
This question already has answers here:
What are the precise semantics of block-level functions in ES6?
(2 answers)
Closed 7 years ago.
What is block scope function in ECMAScript 6?
Can anyone help me understand the main difference in the block scope function compared to ECMAScript 5?
The new let and const in ES2015 (aka "ES6") have four major differences compared with the venerable var:
They have block scope
They aren't hoisted (well, they're sort of hoisted, but in a useful way)
Repeated declarations are errors
When used at global scope, they don't create properties of the global object (despite creating global variables; this is a new concept as of ES2015)
(For what it's worth, this is covered in detail in Chapter 2 of my recent book JavaScript: The New Toys, which covers ES2015-ES2020.)
Block scope
var variables exist throughout the function they're declared in (or globally, if declared globally), they aren't confined to the block they're in. So this code is valid:
function foo(flag) {
a = 10;
if (flag) {
var a = 20;
}
return a;
}
console.log(foo(false)); // 10
console.log(foo(true)); // 20
a is defined regardless of whether flag is true and it exists outside the if block; all three as above are the same variable.
That's not true of let (or const):
function foo(flag) {
if (flag) {
let a = 10;
}
return a; // ReferenceError: a is not defined
}
console.log(foo(true));
a only exists inside the block it's declared in. (For for statements, a declaration within the () of the for is handled very specially: a new variable is declared within the block for each loop iteration.) So outside the if, a doesn't exist.
let and const declarations can shadow declarations in an enclosing scope, e.g.:
function foo() {
let a = "outer";
for (let a = 0; a < 3; ++a) {
console.log(a);
}
console.log(a);
}
foo();
That outputs
0
1
2
outer
...because the a outside the for loop isn't the same a as the one inside the for loop.
Hoisting
This is valid code:
function foo() {
a = 5;
var a = a * 2;
return a;
}
Bizarre-looking, but valid (it returns 10), because var is done before anything else is done in the function, so that's really:
function foo() {
var a; // <== Hoisted
a = 5;
a = a * 2; // <== Left where it is
return a;
}
That's not true of let or const:
function foo() {
a = 5; // <== ReferenceError: a is not defined
let a = a * 2;
return a;
}
You can't use the variable until its declaration. The declaration isn't "hoisted" (well, it's partially hoisted, keep reading).
Earlier I said
They aren't hoisted (well, they're sort of hoisted, but in a useful way)
"Sort of"? Yes. A let or const declaration shadows an identifier throughout the block in which it appears, even though it only actually takes effect where it occurs. Examples help:
function foo() {
let a = "outer";
for (let x = 0; x < 3; ++x) {
console.log(a); // ReferenceError: a is not defined
let a = 27;
}
}
Note that instead of getting "outer" in the console, we get an error. Why? Because the let a in the for block shadows the a outside the block even though we haven't gotten to it yet. The space between the beginning of the block and the let is called the "temporal dead zone" by the spec. Words aren't everybody's thing, so here's a diagram:
Repeated declarations
This is valid code:
function foo() {
var a;
// ...many lines later...
var a;
}
The second var is simply ignored.
That's not true of let (or const):
function foo() {
let a;
// ...many lines later...
let a; // <== SyntaxError: Identifier 'a' has already been declared
}
Globals that aren't properties of the global object
JavaScript has the concept of a "global object" which holds various global things as properties. In loose mode, this at global scope refers to the global object, and on browsers there's a global that refers to the global object: window. (Some other environments provide a different global, such as global on NodeJS.)
Until ES2015, all global variables in JavaScript were properties of the global object. As of ES2015, that's still true of ones declared with var, but not ones declared with let or const. So this code using var at global scope, on a browser, displays 42:
"use strict";
var a = 42; // Global variable called "a"
console.log(window.a); // Shows 42, because a is a property of the global object
But this code shows undefined for the properties, because let and const at global scope don't create properties on the global object:
"use strict";
let a = 42; // Global variable called "a"
console.log(a); // 42 (of course)
console.log(window.a); // undefined, there is no "a" property on the global object
const q = "Life, the Universe, and Everything"; // Global constant
console.log(q); // "Life, the Universe, and Everything" (of course)
console.log(window.q); // undefined, there is no "q" property on the global object
Final note: Much of the above also holds true if you compare the new ES2015 class (which provides a new, cleaner syntax for creating constructor functions and the prototype objects associated with them) with function declarations (as opposed to function expressions):
class declarations have block scope. In contrast, using a function declaration within a flow-control block is invalid. (It should be a syntax error; instead, different JavaScript engines handle it differently. Some relocate it outside the flow-control block, others act as though you'd used a function expression instead.)
class declarations aren't hoisted; function declarations are.
Using the same name with two class declarations in the same scope is a syntax error; with a function declaration, the second one wins, overwriting the first.
class declarations at global scope don't create properties of the global object; function declarations do.
Just as a reminder, this is a function declaration:
function Foo() {
}
These are both function expressions (anonymous ones):
var Foo = function() {
};
doSomething(function() { /* ... */ });
These are both function expressions (named ones):
var Foo = function Foo() {
};
doSomething(function Foo() { /* ... */ });
i am reading Professional JavaScript for Web Developers
i got problem when reading "When the garbage collector runs, it marks all variables stored in memory. It then clears its mark off of variables that are in context and variables that are referenced by in-context variables."
i know when the object could not be reached by any variables, the memory associated would be reclaimed.
What does "variables that are in context" mean? Are they variables that could be found in the scope chain? But what about the "variables that are referenced by in-context variables"?
i am confused.
I'm assuming it's to avoid accidentally deleting variables used in a closure. In javascript, just like any other functional language, just being unreachable is not enough to tell you weather you should delete an object.
Take for example the following code:
function a () {
var x=0;
return function () {
alert(x++);
}
}
var b = a();
// at this point, the instance of `x` created by calling `a` is
// no longer reachable but we are still using it in the closure.
If we follow just the "unreachability" rule then the closure created would lose the variable x.
Consider this:
(function(){
var sobriety = [];
window.inception = function() {
var i = 0,
j = 0,
inner_level = { i: i },
level = { level: inner_level },
food = {};
return function() {
var new_level = {
level: level.level
};
new_level[i] = 'step ' + i;
new_level.level.i = i;
sobriety[i++] = new_level;
};
};
window.show_my_sobriety = function() { console.log(sobriety); };
})();
var agent = inception();
agent(); agent(); agent();
show_my_sobriety();
JS Fiddle.
I admit this example is somewhat sophisticated, but I just had to make it to show the difference between i (a primitive) and inner_level (a reference type).
Here we have a module with one sobriety variable local to it, and two functions made global (by assigning them to properties of window object). Note that these global functions will have access to sobriety variable even after the module which has it defined is finished (in-context).
inception function, when invoked, defines five variables: two scalar (i and j) and three reference (inner_level, level and food), then defines a function and return it.
This function apparently access i and level (the same context), and sobriety (the outer level context) - but not j and food. Hence latter would be collected by GC right after window.inception is complete; the former, though, stay uncollected - because they're referred by the inner functions.
Now the tricky part. While you don't see access for inner_level in this function, it's still accessed - as it's a value of level property of the same-named object. And, when you check the results, you'd see that all three elements have the same level.i value - equal to 2. That's what's understood by "variables that are referenced by in-context variables".