JavaScript Scope: Global Variable Confusion - javascript

I am having trouble understanding why the following code prints 1 and not 10. I thought that console.log would print 10 because if a variable is not declared within a function it is part of the global scope.
If I remove the line, function a(){}, the console.log statement prints 10. Can someone please explain why the presence of this line affects the value of a.
Additionally, if this question can be refined I would appreciate any suggestions
function b() {
a = 10;
return;
function a(){}
}
var a = 1;
b();
console.log(a);

Just add console.log(a); to your function and you will realize that 'a' is a function that has local scope, which means, it inside function b() and can't really be called from outside. Hence, the value of a remains as 1, as nothing really affects it.
function b() {
console.log(a);
a = 10;
return;
function a(){}
}
var a = 1;
b();
console.log(a);

Function declarations create local variables, and all declared variables are hoisted to the top of their containing function scope. Thus, you have something equivalent to:
function b() {
function a(){}
a = 10;
return;
}
which is in turn roughly equivalent to
function b() {
var a = function(){}
a = 10;
return;
}
As you can see in this form, it's clear that a is a local variable, so a = 10 assigns a value to the local a, not the global a.

Because both function declarations and variables are treated similarly under the hood.
When a function is called, its contents are first parsed to configure the stack frame ready for its execution.
As part of this process, function declarations and variables are inserted as identifier/value pairs into the same data structure (Environment Record inside the Lexical Environment) inside the stack frame (Execution Context) created when a function is called.
So by declaring a function a inside b, you create an identifier named "a" in the Environment Record created for the call to b, shadowing a in the outer context.
So a in the outer context remains 1.

Related

Why is the variable described with let accessible inside the function?

In the below code snippet, i is declared after function f, so how can function f access i's value?
let f;
if (true) {
f = () => {
console.log(i)
}
let i = 1;
}
f();
i is declared after function f
Ehm, yes, but in the same scope (block).
i is declared in the same block as the value of f (an anonymous function that uses i), so it is in scope:
f = () => {
console.log(i)
}
// same scope as the definition of the anonymous function:
let i = 1;
Now at the time of declaration, the anonymous function does not need to know what i is, and if you never declared it, it would just resolve to undefined when you do f().
At the time of execution, i is already declared and assigned a value. The anonymous function "recalls" this variable because it was declared in it's scope. This is called a closure.
let keyword scope.
First of the let keyword scope for block of code in your program 'i' is inside of if block so 'i' value scope is inside of if block.
so,if you create 'n' number of function inside of if block it'll access the 'i' value.
please check my example remove the comment in f1() inside console.log(j) and put debugger you'll understand clear.
Here, you'll get error because 'j' is only access for f() means 'j' scope only inside of f(). If you use inside of f() it'll access. Check f2() it's able to access 'j' value.
<script>
debugger
let f,f1,f2;
if (true) {
f = () => {
console.log(i)
let j=11;
f2=()=>{
console.log(j);
}
}
f1=()=>{
console.log(i);
//console.log(j);
}
let i = 1;
}
f();
f1();
f2();
</script>
You are correct that the variables declared by lexical let declarations may not be accessed without error until after the declaration lexically occurs. (This is in contrast to var declarations, which make their declared variable accessible and referencible starting from the top of their containing scope, regardless of where the declaration occurs within that scope.)
The variables referenced by name in f are not accessed until f is actually called. The JavaScript engine does not ask the question "In what scope is the variable i and what is its value?" until f is executed. At the chronological time f actually is executed, the surrounding block scope has had the variable i made accessible within it, so f can use that variable to resolve the identifier i.
When you refer to i at execution time, the JavaScript engine looks up the scope chain to find the nearest declared variable named i. The declaration let i is an instruction to the JavaScript engine to make available a variable named i to the current block scope. The fact that the surrounding scope has no accessibl i at the time f is defined is immaterial; what matters is that the scope does have a variable i by the time f is executed.
Consider this example:
var f;
if(true) {
f = ()=>{ console.log(i) }
try { f(); } catch(e) { console.log(e); }
let i = 1;
try { f(); } catch(e) { console.log(e);}
}
The first call produces an error, because during the execution of f, no i has yet been made accessible, while the second execution, after the let i statement, runs without error.
As Javascript is an interpreted language, it will execute statements one at a time, line by line, without looking at the entire code first (unlike compiled languages). What this means is that your function will not throw an error at the line console.log(i); before runtime.
At runtime, you are defining and initializing i before calling the function f, so by the time f is called, the value of i is already known.
Regarding scopes, i and f are declared in the same block (not inside a function or anything), therefore f can access i fine. No scope issues there.

Why can't I access this particular variable when referencing its outer environment?

I'm learning about lexical scope and execution contexts in JS and came across a question.
My understanding of the keyword this may be limited, but I see that it references all variables in any function's current execution context.
Consider this example:
function b(){
console.log(this);
}
var myVar = 1;
b();
Here, I'll get a console log to myVar which will be assigned to the value 1.
Now for this example:
function a(){
function b(){
console.log(this);
}
var myVar = 2;
b();
}
var myVar = 100;
a();
When function b is called, I see a reference to myVar, which is assigned to 100. Why isn't there a reference to myVar as assigned to 2?
Doesn't 'this' refer to the current function's lexical environment? In this case, function b is enclosed in function a and not the global environment.
Your understanding of this is completely wrong.
The value of this depends (usually) on how the function was called. (See How does the “this” keyword work? for details).
If you are:
in a browser
your JS isn't running in strict mode (which it should be)
the function is called with no explicit context
… then this will be the window object.
Normally, when a variable is declared, it exists only in the scope in which it was declared. It is not a property of any object.
There is one exception:
When a variable is declared in the global scope (i.e. outside of any function or without let or var inside a function) then it also becomes a property of the window object.
The effect you are seeing is the combination of those two things.
b(); has no context, so this is window. var myVar = 100; is outside of any function, so is a global and thus a property of window.

Javascript - outer reference of a function shows "not defined"

i have the following code:
function log(a) {
var b = 5;
a();
}
log(function(){
console.log(b);
});
When that anon function is executed at function log, I am getting "b is not defined". Well, It seems like that anon's outer environment reference isn't log's, as If it wasn't created inside of it so therefore it can't find that var. so where is it being created? on the global level? My initial thought was that those parentheses make the anon function created inside of log's.
Every time you call a function, assuming that you do not declare global variables (you didn't declared any), the scope is created for that function and what goes in that scope is determined not by where the function was called but by where it was defined. You can see that where you defined the anonymous function (in the call to log), the variable b is not in that scope that is why it's not available.
Let's rewrite your code:
function log(a) {
var b = 5;
a();
}
function logger() {
console.log(b);
}
log(logger);
You can see that your code and mine are doing the same thing, the only difference is that mine code doesn't have an anon function. They do not share common variables in their scopes.
Now check this out:
var b = 5;
function log(a) {
a();
}
function logger() {
console.log(b);
}
log(logger);
Now both log and logger share a common variable b in their scopes (log is not using b so if you check it in a debugger it will be undefined). As I say you don't determine scope by where function was called but by where and how it was declared.
In JavaScript scopes exists within functions. So your b variable is visible only within its scope - the anonymous function. If you want it to be visible outside the function then you can assign the variable to the global scope.
function log(a) {
window.b = 5;
a();
}
log(function(){
console.log(b);
});

Global variable override with a local one

The following test is a success, and prints 1 and 1:
function test1() {
a = 1;
console.log(a); // prints "1"
}
function test2() {
console.log(a); // prints "1"
}
test1();
test2();
And the following test fails, because a local variable overrides the previously created global one:
function test1() {
a = 1;
var a = 2;
console.log(a); // prints "2"
}
function test2() {
console.log(a); // throws an error
}
test1();
test2();
Why does the second example remove the global variable permanently? What purpose/logic does such functionality serve in JavaScript?
EDITED: For those who marked it as a duplicate of Strange Behavior on Global and Local Variable in JavaScript
Hoisting refers to the scenario when a later declared local variable is moved up the function scope, overriding the previously visible variable. But in our case the function first creates a global variable, and then deletes it completely/globally.
However, it is probably related, as we can see in the following test:
function test1() {
a = 1;
console.log(a); // prints "1"
}
function test2() {
console.log(a); // prints "undefined"
var a = 2;
}
function test3() {
console.log(a); // prints "1"
}
test1();
test2();
test3();
In your second case, no global variable named a ever exists.
a = 1 generally does not create a global variable. The statement a = 1 stores the value 1 in a variable called a. If the variable environment of any containing local scope has a variable called a, the nearest scope with an a variable will have that variable set to 1.
As a special case, if a variable called a does not exist in any containing scope (and if you are not in strict mode), then the JavaScript engine will create a global variable called a.
In your second case, var a creates an a variable in the local scope. Due to hoisting, that modification to the scope's variable environment happens before any code runs. Thus, by the time a = 1 is executed, that local scope does have a variable called a, so that local variable is used.
In your third case, test2 logs the local variable a (created by the var a). At the time the log call is made, the local a has not yet been assigned a value.
Hoisting refers to the scenario when a later declared local variable is moved up the function scope, overriding the previously visible variable.
Hoisting simply means that all var declarations are treated as if they happen at the top of their containing function. It has nothing to do (directly) with overriding variable visibility.
Note that declarations with an assignment will have only the declaration hoisted, not the assignment. The assignment stays in place wherever it is in the function.
Javascript moves variable declarations to the top. So in this example the compiler reads you code as:
function test1() {
var a;
a = 1;
a = 2;
console.log(a); // prints "2"
}
function test2() {
console.log(a); // throws an error
}
test1();
test2();

when should I put var in front of function in js?

<script type="text/javascript">
var output = function() {console.log('result')}
output();
</script>
If I changed to output = function() {console.log('result')}; it still shows the right result, so my question is:
what is the difference between them? when should I put var in front of function in js? is that the same principle as var in front of variable?
A function defined in a script tag is in the global scope (ie the window object in a browser context) so there is no difference in this case.
Inside a function block, however, is a different story. For example:
foo = function() {
var foo = 1;
console.log(foo);
}
foo(); // logs '1'
foo(); // logs '1'
But:
foo = function() {
foo = 1;
console.log(foo);
}
foo(); // logs '1'
foo(); // SyntaxError: Unexpected token function
Because foo wasn't defined locally, we overwrote the global object.
You're in the global window scope, so there's no difference.
It doesn't matter what the type of the variable is.
If this is declared in functions, then there is a difference:
function name(){
var a=1;
}
alert(a);
Here a will be undefined, as var declares the variable in the scope of the function.
As an excercise:
var a=2;
function name(){
var a=1;
}
name();
alert(a);
This alerts 2 instead of 1, since the middle var belongs in the scope of the function, which is separate from the global scope.
You can also modify global variables this way:
var a=2;
function name(){
a=3;
}
name();
alert(a);
Also compare this with let, which limits it's scope to the block instead: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
"Is that the same principle as var in front of variable?"
Yes. output is a variable.
So I would suggest you use var in front of it when you define it. You could eventually change its value without using var. As in:
var A=1;
A=2;
A=A+1;
Consider the "script" of the funcion as the "value" of that variable.
var F=function() {console.log('one')};
// change it later
F=function() {console.log('two')};
(not suggesting you do this, but to show you it is 100% a var)
You are actually assigning to the variable named "output" a value of "function() {console.log('result')}" not as a string but as a script that gets executed. Note the semicolon at the end like in var A=3;
Now "inside" output there is the code that executes console.log('result'). (more or less, just to explain).
As you usually do not change that same function later (you can, and sometimes it is done) I really suggest you use var in front of it every time you define a function like this, even in cases when it is not strictly necessary, just to be safe you do not override an existing function.
This is a bit different from defining the function as:
function output() {console.log('result')}
Here there is no = sign, no assignment, no semicolon at the end. This is not a variable assignment but a function "definition" although results are similar, and you can call output() in both cases, there are differences. The main one I think is that function definitions are examined before executing the script line by line, while with assignment you really need to have the assignment line processed before you can use the function. So this:
output();
function output() {console.log('result')}
works. While this:
output(); // nope! output not defined yet!
var output=function() {console.log('result')}
doesn't. Variables are assigned or changed when the assignment instruction is read and interpreted.
// here A is undefined, B() prints 'ok'
var A=function() {console.log('first')};
// here A() prints 'first', B() prints 'ok' as usual
A=function() {console.log('second')}; // change it
// here A() prints 'second', B() prints 'ok' as usual
function B() {console.log('ok')}
// same here A() prints 'second', B() prints 'ok'
Without var your variable will be declared as global variable which means it is available on other JS files too. In short If you declare a variable, without using var, the variable always becomes GLOBAL.
Generally there is no difference because you are in the global scope, but in ES5 there's a strict mode, which slightly changes the behavior of undeclared variables. In strict mode, assignment to an undeclared identifier (not putting var in front) is a ReferenceError.
For example:
"use strict";
myVariable = "foo"; // throws a ReferenceError
Function or not function, here's what MDN has to say about var:
The scope of a variable declared with var is the enclosing function or, for variables declared outside a function, the global scope (which is bound to the global object).
Using var outside a function is optional; assigning a value to an undeclared variable implicitly declares it as a global variable (it is now a property of the global object). The difference is that a declared variable is a non-configurable property of the global object while an undeclared is configurable.
And you could also read about the function statement here and the function operator here.
Cheers!

Categories

Resources