I was reading about hoisting in mozilla, and noticed an example explaining how a variable can leak outside of the function scope:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#Initialization_of_several_variables
The example says,
var x = 0;
function f(){
var x = y = 1; // x is declared locally. y is not!
}
f();
console.log(x, y); // 0, 1
// x is the global one as expected
// y leaked outside of the function, though!
I don't understand what is happening here. I am looking for a technical explanation more than anything. How is y accessible outside at this point?
Edit: I understand how this function is behaving, and I should clarify and say I wanted an understanding of what is happening in the code and memory (ex, pointers, etc..).
you have var x = y = 1 which means that x is declared inside but y is not, var doesn't apply to y in this case, that's why it's leaked outside the function
This is breakdown of the statement:
var x = y = 1;
\___/ \____/
identifier initializer
\____________/
variable declaration
In this case the initializer is another assignment. Due to variable hoisting, this statement is evaluated as follows:
var x;
x = y = 1;
As you can see, y is not declared. Assigning to an undeclared variable creates a global variable (implicitly).
var x = 0;
function f(){
var y = 1;
var x = 1;
}
f();
console.log(x, y);
Try the above code. The problem is that you didn't include a var in front of y. The result is that y is hoisted into global scope.
In JavaScript, variables are declared as soon as you use them, like this:
function f(){
y = 1;
}
The problem is that y is not scoped within f(), like it would be in most other programming languages. So you can do something like this:
function f(){
y = 1;
}
console.log(y);
And you will see 1 on the console.
This is generally considered a bad practice, because it pollutes your namespace and can lead to subtle, hard to track down bugs.
To avoid this, always use let or var to explicitly declare variables, like this:
function f(){
let y = 1;
}
// the y declared in f() is not accessible out here
console.log(y)
This should give you an error like: "Uncaught ReferenceError: y is not defined" on your console.
For more details, read this page, specifically the section 'Automatically Global': http://www.w3schools.com/js/js_scope.asp
In the example you gave above the problem is more subtle, because x is scoped properly, but the short-hand declaration of y makes it globally scoped:
function f(){
let x = y = 1;
}
EDIT 2022: Updated to recommend let over var. Here is an explanation of the difference.
Related
Run below code in Node environment. Running it in browser console doesn't allow to redeclare variable of var.
console.log(a);
var a = 5;
According to hoisting, the above code is going to look like this
var a = undefined;
console.log(a); // undefined
a = 5;
a variable is being hoisted to the top of the file. JS Engine allocates memory for this variable before the execution. The question is why below code consols 5 instead of undefined.
var a = 5;
console.log(a);
var a = 6;
I'm looking at this code and imagining that it's going to look like this:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
I'd like to be sure of the answer instead of guessing. The JS Engine is smart enough to see that a variable is already declared and is going to ignore the next var expression and rehoisting in such case? So the output should be looking like:
var a = 5;
console.log(a); // 5
a = 6;
So it's like:
JS Engine sees for the first time declaration (in this case along with initialization) of a variable so it's allocating memory.
JS Engine sees for the second time declaration of a variable but is going to ignore the hoisting because variable of given name is already in the memory.
Am I wrong in something?
Preface: In modern JavaScript, var should never be used. Use let or const.
The JavaScript engine handles var in two steps:
Upon entering the global scope or a function scope, it processes every var in the entire scope, defining variables for the them initialized wit the value undefined. If a variable is declared more than once with var, it's exactly as though it were declared once.
Then it starts the step-by-step execution of the code. In that step-by-step execution, any initializer on a var statement (the = 5 in var a = 5) is considered an assignment. So var a = 5 is treated exactly like a = 5 at this point.
So in your example:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
It's as though you had written this:
var a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
var a = 6;
or this:
var a;
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
var a;
or even this (but please don't! :-) ):
var a = 5;
var a = undefined;
console.log(a); // undefined
var a = 6;
var a;
That is, first all the variables declared with var are created (and only once), then the code runs as though any initializers on them were assignments.
This is not how let, const, and class declarations are handled (collectively: lexically-scoped declarations). First: Multiple declarations in the same scope are an error (including if one of them is with var and the other is with one of the lexically-scoped ones). Second: They're hoisted (in Step 1 above), but the hoisted binding¹ is uninitialized until the declaration is executed in the step-by-step code, at which point it's initialized (either with the value from the initializer, or with undefined if it's just e.g. let a;). The time between entry to the scope and the point the binding is initialized is called the Temporal Dead Zone. var doesn't have it because var variables are initialized when they're created (with the value undefined), but let, const, and class declarations do.
¹ The term binding is the general term for variable-like things. In the code:
function example(p) {
var v;
let l;
const c = 42;
function f() {
}
class C {}
}
the bindings created upon entering the example function's scope are p (a parameter), v (a var variable), l (a let variable), c (a const constant), f (the binding created by a function declaration), and C (the binding created by a class declaration). (Note: function and class expressions are handled slightly differently.)
I'd like to understand the debugging content during the execution of the following code:
var z = 2;
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
var x = 3;
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
var squareoftwoplusother = (SQUAREPLUSOTHER)(x);
x = 4;
z = 4;
var result = squareoftwoplusother(5);
console.log("result", result);
Now....
At the call of:
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
the debug shows clearly:
squareoftwoplusother: undefined
SQUAREPLUSOTHER: x=>...
x: 3
z: 2
at the following call of:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
the debug shows:
Local:
x: 3
Closure
z: 2
at the following call of:
x = 4;
z = 4;
the debug shows:
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
x: 4
z: 4
at the following call of:
var result = squareoftwoplusother(5);
the debug shows:
result: 18
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
Local:
x: 5
Closure
x: 3
Closure
z: 4
and at the final calls the debug shows:
result: 18
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
x: 4
z: 4
now the questions:
How many "Closures"? They belongs to? (i.e. how to explain...)
Local:
x: 5
Closure
x: 3
Closure
z: 4
How the scope of the variables is managed in javascript?
How to have a "definitive" idea in terms of "context" or "whatever it be" about the mess of closure :-)?
thanks in advance
Ed
Every time a function is called, a local scope is created for that function. If there is still a reference to that local scope after the function returns, then a closure is created. Once the reference is gone, the closure is also garbage collected.
If you add breakpoints to the first line of every function, you will see that scopes are created that may later become closures.
A great explanation is given here http://dmitrysoshnikov.com/ecmascript/javascript-the-core/#activation-object
I'll add some braces to your code to make that easier and I'll add self calling function to add a level of closures.
(() => {
var z = 2;
var SQUAREPLUSOTHER = (x) => {
debugger;
return (y) => {
debugger;
return (x * x) + y + z;
}
};
var x = 3;
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
var squareoftwoplusother = (SQUAREPLUSOTHER)(x);
x = 4;
z = 4;
var result = squareoftwoplusother(5);
console.log("result", result);
debugger
})()
The screenshots below are from Chrome DevTools which minimizes the closures, that is, it only maintains references to what is needed. Back in the days, the entire closure was always available and you could prove it because it showed up in the closures pane of debuggers. See Javascript closures performance for further details
Scope
There are just three different scopes in your code. One is the global scope all variables you declare in and the other two are here:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
There are two function scopes, one containing the variable x and the other function scope containing the variable y. It might be easier to see if you replace the arrow function with functions:
// global scope
function SQUAREPLUSOTHER(x) {
// function scope containing x
return function(y) {
// function scope containing y
return (x*x) + y + z;
};
}
How scoping works is actually quite simple:
Every { starts a new scope and its counter } closes it, variables declared inside it with let and const (or var but thats slightly more complicated) are part of the scope. If it is the scope of a function, the parameters (e.g. x and y) are part of that scope.
From the code, you can access the all variables that are in the current scope or in its parent scope, if there are multiple with the same name you get the most inner one. Therefore x inside of the SQUAREPLUSOTHER function refers to the variable of the SQUAREPLUSOTHER scope, while for code outside of it x is the global variable and the functions variable can't be accessed.
The Scope of a variable does not change at runtime, you can always directly see which scope a variable belongs tp by looking at the surrounding { .. }.
Now different variables in different scopes have to hold values at runtime, thats where we get to:
the environment record
When you call a function, the JavaScript engine creates a new "EnvironmentRecord" (which is like an internal object) that contains all the variables of the function you call, e.g. in this case:
function test(a) {
let b;
}
Then if you call that function (test(1)) a new environment record gets created that contains a and b. Now the code inside the function gets run, and every variable is looked up in it. If there are two functions nested into another, calling the inner function will create an environment record that holds a reference to the outer one:
function test(a) {
function test2(b) {
}
test2(5);
}
Now calling test(1) will create an record where a is 1. If the engine then executes the second call (test2(5)) it creates another record containing b being 5, and that holds a reference to the record containing a. Now if you use a inside test2, the engine will look it up in the current environment record, won't find it, and then look it up in the parent where it finds a being 1.
closure
Usually those records get deleted when the execution reaches the }, however if there is another record that got the current record as a parent, it won't get deleted. It will exist until all child records got deleted, then it will also remove the parent. This behaviour (variables live longer because they can be accessed from an inner functions body) is called a closure.
This question already has answers here:
Are variables declared with let or const hoisted?
(7 answers)
Closed 4 years ago.
var x = true;
if (x == true) {
console.log(typeof(x));
let x = false;
};
console.log(x);
Sorry new to programming and getting this error:
Error: x is not defined.
I am extremely confused.
It doesn't look like it, but the two variables you declared are different. Your var declaration is one of them, and the let declaration is different. You've confused yourself by naming them with the same variable.
The let x exists strictly within the if block. This let x is the only x that can exist in that block now and you've tried to use that x before you declared it when you attempt a console.log.
You can get away with using variables before you declare them with var, but let and const won't let you.
Also, I highly recommend you don't mix var and let in your code. You have to really understand what's going on if you do.
var x = true; // first x variable
if (x == true){ // first x variable
console.log(typeof(x)); // second x variable, used before declaration
let x = false; // second x variable created in `if` block
};
console.log(x); // first x variable
edit:
Your follow up question is still 2 different variables. The only thing you've done is coerced the code to make it look EVEN MORE like the same variable.
var x = true; // first x
if (x == true) { // first x
let x = false; // second x
console.log(x); // second x, false
};
console.log(x) // first x, true
This one's a little tricky to explain, but I'll try;
var x = true;
if (x == true) {
console.log(typeof(x)); // <-- Error: x is not defined.
let x = false;
};
console.log(x); // true
The last line is actually fine because you defined x up top.
What's happening inside the if statement is that JavaScript looks ahead and sees that you've declared x (with let or const) somewhere in that scope, but it hasn't yet reached that line.
So merely having a let or const somewhere further down in the same scope is causing that variable to not be defined.
If you move it up a line, everything is fine:
var x = true;
if (x == true) {
let x = false;
console.log(x); // false
};
console.log(x); // true
And if you don't redeclare x, it's also fine:
var x = true;
if (x == true) {
let y = false; // different variable name (won't "shadow" the `x` from above)
console.log(x,y); // true false
};
console.log(x); // true
var behaves differently:
if (true) {
console.log(y); // undefined
var y = false; // `var` is less strict. The declaration of `y` is hoisted to the top of the scope, but it's not assigned until this line
console.log(y); // false
};
The part of explanation that everyone is missing is that declarations are hoisted, which means that the declaration is brought to the beginning.
This is especially true with var. For let things are a little bit different and the term hoisting is not the most accurate. But it is useful to understand what is going on.
When executed, your code will look something like this (not exactly but gives the idea)
var x = true;
if (x == true) {
let x // hoisted at top of scope, for let scope is this if block
console.log(typeof(x));
x = false;
};
console.log(x);
Now everything should make sense: typeof(x) refers to the x declared with let but not yet initialized.
To be clearer this is the reason why the x inside of typeof refers to the x declared in the row below and not the one at the beginning: because the declaration of the existence of x inside that block is brough to the beginning of such block
I'm doing some scope exercises and came across the following question.
I honestly thought the answer was 30. I cannot figure out why result is equal to 10. To me, it seems like x would come from the global scope of 30.
I would appreciate any insight on this matter. Thanks so much.
var x = 30;
function get () { return x; }
function set (value) { x = value; }
set(10);
var result = get(20);
console.log(result);
x does comes from the global scope, but before you call var result = get(20);, you call set(10); which assigns 10 to the global x.
x does come from the global scope. function set (value) { x = value; } changes the value of x in the global scope.
The W3Schools example for a JS for-each loop (http://www.w3schools.com/js/js_loop_for.asp) is
var person = {fname:"John", lname:"Doe", age:25};
var text = "";
var x;
for (x in person) {
text += person[x];
}
and I'm wondering if there's any particular reason why x is defined outside of the loop if it's only used inside the loop in that particular example, or if that's necessary condition for defining a for-each loop, and if so, why?
This is(/was) an old convention, meant largely to indicate that x does not exist only inside the loop.
Declaring variables at the top of a function is not necessary and is variously encouraged/discouraged by different style tools. It can be convenient to group variable declarations in one place, but may increase the distance between declaration and use (generally considered a bad thing).
In JS, the var declaration works at function-scope, so variables are hoisted to the top of the nearest function and live for the duration of the function. See:
function foo() {
for (var i = 19; i > 0; --i) {
console.log(typeof i); // number
}
console.log(typeof i); // number
}
foo();
Because the variable is accessible throughout the function, it is declared at the start of the function (C used to require this).
ES6 has changed this with the let statement, which introduces the more common block-level scoping. With let, the variable is only available inside of the loop:
function foo() {
for (let i = 19; i > 0; --i) {
console.log(typeof i); // number
}
console.log(typeof i); // undefined
}