JavaScript: how are functions and their scopes/lexical environments passed around? - javascript

I'm playing with closure and have no problem understanding how inner functions have access to outer lexical environments:
function outer() {
var bar = 0;
function inner() {
console.log(bar);
}
inner();
}
outer(); // prints '0'
But what gets me is this:
function foo() {
return function inner() {
console.log(bar);
}
}
function outer() {
var bar = 0;
var fakeInner= foo(); // not the same as "var fakeInner= function () { console.log(bar); }"
fakeInner();
}
outer(); // ReferenceError: bar is not defined
Here I try to "define" an "inner function" fakeInner by assigning it to a function expression returned from an outside function.
I used to think that JavaScript creates the fakeInner inside with a "copy" of its code, something like: var fakeInner= function () { console.log(bar); }, which would then have access to bar, but this is not the case -- it appears when fakeInner is invoked, JavaScript traces back to its definition. Am I understanding this correctly? That is, function objects (either declarations or expressions) are mere references to their definition spots, and passing them do not change their lexical environments (hence inner functions have to be defined inside with explicit syntax)?

Yes, functions do only have access to their own lexical environment - from where they were defined. JavaScript does not have dynamic scope where a function has any kind of access to the scope where it is called.
This happens by attaching the scope to the function object, it is a part of the closure.

In this example,
function outer() {
var bar = 0;
function inner() {
console.log(bar);
}
inner();
}
outer();
scope of bar is outer function and will be available for all function defined in it.
function foo() {
return function inner() {
console.log(bar);
}
}
function outer() {
var bar = 0;
var fakeInner = foo(); // not the same as "var fakeInner= function () { console.log(bar); }"
fakeInner();
}
outer();
In this example, again bar is accessible inside outer but foo is outside it. Hence reference error is thrown.
Also refer What is the scope of variables in JavaScript? for more information.

The scope of inner functions is its own outer function. Not the function it is being called from.

Related

JavaScript Hoisting - Hoisted code after Memory Creation phase

I was reading Kyle Simpson book: https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/scope%20%26%20closures/ch4.md#functions-first.
But I don't fully understand this line "Notice that var foo was the duplicate (and thus ignored) declaration, even though it came before the function foo()... declaration, because function declarations are hoisted before normal variables."
Let's say this is the code:
console.log(foo); // The output is: foo() { return 2; }
function foo() {
return 1;
}
function foo() {
return 2;
}
var foo = 3;
I want to visualize what would be the output in JS Engine after Memory Creation phase. Will it be like this?
function foo() {
return 2;
}
console.log(foo);
If yes, why var foo = 3; was ignored? There is no duplicate for var in the snippet. If no, can anyone please help me visualize what would be the output in JS Engine after Memory creation phase?
Thanks
I think the text refers to
// scope creation
var foo; // the name was declared. Thrice.
foo = undefined; // from the var
foo = function foo() { return 1 }; // from the first declaration
foo = function foo() { return 1 }; // from the second iteration
// execution
console.log(foo);
;
;
foo = 3;
where foo is initialised with undefined due to the var foo declaration, but then gets overwritten by the initialisation value from the function foo declaration which takes precedence - regardless of the order in which the declarations appeared in the code, function declarations overrule var declarations.
Function declarations
Declare a variable with the same name as the function (this is hoisted)
Assign the function to that variable (this is hoisted)
var statements with associated assignments
Declare a variable with that name (this is hoisted)
Assign the value to that variable (this is not hoisted)
var foo = 3 is not ignored.
The variable declaration (var foo) is ignored because the earlier function declarations already declared that variable. (i.e. because it duplicates the declaration of foo).
foo = 3 is not ignored and does assign 3 to foo … it just does so after your console.log statement runs because it isn't hosited.

Way to understand this code. How is it working?

I was exploring scopes in javascript and came to know this problem. I don't understand how this problem is working.
function checkType() {
return foo;
foo = 10;
function foo() {};
var foo = 11;
};
console.log(typeof checkType())
My question is this how does javascript compiler decide to return function not variable. Any reference or explanation are welcome.
This is how the compiler will compile the above code..
function checkType() {
var foo = function() {}; /* function will be hoisted to the top,
and will be assigned to the variable as
the name is the same for the two.. */
return foo;
// Code will never reach here as you are returning before this
foo = 10;
foo = 11;
};
console.log(typeof checkType());
Functions which are defined using function() syntax will be hoisted, in this case, the nested function will be hoisted insde the checkType() and hence, the checkType() returns the function instead of the integer.
Note: Because the function was defined using function(){} syntax, it
was hoisted to the parent function scope, else, if the function was
defined using var foo = function() {} then the hoisting would not
have worked in the same way, your function would've returned undefined instead.
More reference on Scoping & Hoisting
http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/
First all the function declarations are hoisted in a scope. So first of all the code moves the variable foo to the top of the scope and initialize its value to function.
The second declaration of variable is not hoisted because the function declaration is already hoisted. So the code is same as
function checkType() {
var foo = function(){}
return foo;
foo = 10;
foo = 11;
};
When we run a code there are two phase of it first is creation phase in this phase the syntax parser will read the code and hoist the function and variables, and second phase is execution phase in which the values are assigned to hoisted variables,
A point to note here is function are stored in memory during creation phase as it is where as the variables are hoisted but values
are not initialized ( which will be assigned during execution phase )
Compiler will treat your code like this after hoisting
function checkType() {
var foo = function() {}; //hoisted function
return foo;
foo = 10;
foo = 11;
};
console.log(typeof checkType(), '\nRturned value from function --->', checkType())
If you define your function as variable than it will be hoisted only but will not be initialized with value, you can see the below example
function checkType() {
return foo;
foo = 10;
var foo = function foo() {};
var foo = 11;
};
console.log(typeof checkType(), '\nRturned value from function --->', checkType())

Test whether variable declared yet in containing scope

I have an inner function that references a variable initialized by its containing outer function:
function outer() {
function inner() {
if (foo) { ... }
}
let foo = 'bar';
inner(); // no error
};
However, there are some circumstances where the inner function may be invoked before the statement defining foo has executed. In this case, any reference to foo causes a ReferenceError in the inner function:
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am within the enclosing block when executing the inner function.
More surprisingly is that even attempting to detect this situation with the typeof operator - which I always understood to be a safe way to test for undefined variables - causes the same ReferenceError:
function outer() {
function inner() {
if (typeof foo !== 'undefined') { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
Edit: I now understand that this behavior is the result of the "temporal dead zone" involving let and const variables discussed elsewhere. However, I'm still looking for a clean, safe way to handle the situation.
Is there any safe way to test whether a block-scoped variable (e.g., one created with 'let' or 'const') has yet reached its declaration?
One brute-force approach is to simply catch the exception thrown by typeof in the "temporal dead zone" before foo has been initialized:
function outer() {
function inner() {
let fooExists = false;
try { fooExists = typeof foo !== 'undefined' } catch(e) {}
if (fooExists) { /* ... */ }
}
inner(); // no error
let foo = 'bar';
}
It is also possible to use var instead of let to work around this issue. Because var is function-scoped, the declaration will be hoisted to the top of outer, making the variable available (though undefined) at all times outer is executing:
function outer() {
// due to hoisting, there is a logical `var foo;` here
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error; `foo` has been hoisted
var foo = 'bar';
}
A similar approach could be taken by putting the declaration (but not initialization) of foo at the top of the outer function while still using let:
function outer() {
let foo;
function inner() {
if (typeof foo !== 'undefined') { /* ... */ }
}
inner(); // no error
foo = 'bar';
}
This last solution would seem to be the cleanest solution for the example given in the question. Unfortunately, it cannot be used when using const variables.
function outer() {
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
let foo = 'bar';
};
This is surprising to me, given that foo has block scope, and I am
within the enclosing block when executing the inner function.
Because of hoisting, the declaration for foo is hoisted to the top of the block, but not the initialization of the variable. It executes as if you had written:
function outer() {
let foo; // declaration only, so still undefined value
function inner() {
if (foo) { ... } // ReferenceError: foo is not defined
}
inner();
foo = 'bar'; // Not initialized until after you call inner!
};
Simply move the initialization up in the block and it will work:
function outer() {
let foo = 'bar'; // declared and initialized
function inner() {
if (foo) { ... } // works as expected
}
inner();
};

Hoisting of functions inside conditionals

I understanding how hoisting in javascript occurs, functions are hoisted before variables, and only the declarations are hoisted. But When I came across hoisting inside if/else conditionals, like this one:
foo(); // "b is the output"
var a = true;
if (a) {
function foo() { console.log("a"); }
}
else {
function foo() { console.log("b"); }
}
Now the conditional is true, so according to the if block, a should have been the output, but due to some kind of hoisting I assume b is the output.
So how is b the output?
In JavaScript, variables, function expressions and function declarations are hoisted to the top of the scope.
Function declarations defines a named function variable without requiring variable assignment.
And important to know is that the entire body of the function declaration gets hoisted up the scope.
E.g.
function outerFunction() {
console.log(typeof functionDeclaration); // outputs "function"
function functionDeclaration() {
// ... function body
}
}
This is because, because of hoisting the code runs like so:
function outerFunction() {
function functionDeclaration() {
// ... function body
}
console.log(typeof functionDeclaration); // outputs "function"
}
In your case, the last function declaration for foo is hoisted to the top of the scope overriding all the other function declarations. Therefore, it logs "b".
Variables and function expressions, however, get hoisted without their assigned values.
E.g.
function outerFunction() {
console.log(functionExpression); // outputs "undefined"
var functionExpression = function () {
// ... function body
}
}
Runs more like so,
function outerFunction() {
var functionExpression = undefined;
console.log(functionExpression); // outputs "undefined"
functionExpression = function () {
// ... function body
}
}
(Ignoring the slightly dodgy behaviour that certain old browsers may have had:)
In Javascript, function statements are scoped within the containing function (or globally if there is no containing function), they're not scoped within an if or else or loop block. So you can't declare a function conditionally in that manner (it can be done another way; see below). And if you declare more than one function with the same name in the same scope the later one will overwrite the first one.
So what happens with your code is:
Both function statements are hoisted, but
They both have the same name so the first is overwritten by the second.
The variable, a is created but not yet assigned a value.
The foo() statement is executed, logging "b"
a is assigned the value true.
The if is executed. The condition is true, but neither the if nor else branches actually do anything because they don't contain statements other than the function declarations that were hoisted earlier.
If you want to create functions conditionally you have to declare a variable and then assign a function expression to it. And then you can not call the function until after that assignment:
var foo;
var a = true;
if(a)
foo = function() { console.log("a"); };
else
foo = function() { console.log("b"); };
foo();
The is not the correct behavior anymore in modern JS browsers! The modern browsers will compile and hoist the mentioned code like this:
// hoisting (treat foo declarations as a normal variable declaration inside if statement)
var a;
var foo;
// now the assignment
foo(); // TypeError (foo = undefined)
a = true;
if (a) {
function foo() { console.log("a"); }
}
else {
function foo() { console.log("b"); }
}
This is a unspecified behavior, different browser behaves diffrently.
MDN explain
In chrome & firefox, it will output foo is not a function.
And in safari, it will output b.
another doc of MDN

Output of below code statements

var abc = function() {
console.log(1);
}
function abc() {
console.log(2);
}
abc();
I am expecting that it will console 2, but no it will console 1 because of
function and variable hoisting.Anybody who want to make it more clear.
In JavaScript, a name enters a scope in one of four basic ways and the order of hoisting follows following order...
Language-defined: All scopes are, by default, given the names this and arguments
Formal parameters: Functions can have named formal parameters, which are scoped to the body of that function
Function declarations: These are of the form function foo() {}
Variable declarations: These take the form var foo;
Reference: http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
In your case the 1st function is type 4 and the second function is type 3. Therefore the second function is hoisted first assigning the function reference to abc. And then the first function is hoisted reassigning the reference to abc. Your code is compiled in following order,
function abc() {
console.log(2);
}
var abc;
abc = function() {
console.log(1);
}
abc();
Note that only declarations are hoisted. Assignments are not hoisted.
var bar = 'test';
var foo = function(){/*foo 1*/};
var foo;
function foo(){/*foo 2*/}
alert(foo);
will be complied in this order,
function foo(){/*foo 2*/} //function declaration
var bar; //variable declaration
var foo; //variable declaration
bar = 'test';
foo = function(){/*foo 1*/};
alert(foo);
var hoisting
the Key to understanding how scope in JavaScript works is understanding the concept of hoisting.
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.
console.log("function hoisting:::: ",abc()); /* log = 2 we are calling the function(hoisting)*/
var abc = function() {
console.log(1);
};
console.log("var:::: ",abc()); /* log = 1*/
function abc() {
console.log(2);
}
abc(); /* log = 1*/
console.log("function:::: ",abc()); /* log = 1*/
So in your case you are not calling the function, to call the function log it on the top.
this is how the javascript engine interprets it
function abc() {
console.log(2);
}
var abc;
abc = function() {
console.log(1);
};
abc();
REF: JavaScript Scoping and Hoisting
If you're simply asking why the function abc isn't the one being executed despite it being declared most recently, it's because, In Javascript, functions are hoisted to the top of the current scope.
That means your code is morphed into something like the following:
function abc() { console.log(2); }
var abc = function() { console.log(1); }
abc();
That means, when it comes time to call abc, the "active" one is the one that logs 1.
One of the best articles I ever found on this behaviour was the one over at adequately good.

Categories