I'm not able to understand the following code
function test() {
let a = b = 0;
a++;
return a;
}
test();
typeof a;
typeof b;
getting type of a as undefined whereas typeof b as "number"
The main difference is scoping rules. variables that are declared by the keyword var are scoped to the immediate function body while let variables are scoped to the immediate enclosing block denoted by { }, meaning blocker scoped.
This is the reason why let keyword was introduced to the language. the function scope can be very confusing and can cause many bugs in JavaScript.
So, what you're seeing in the code is a decleration of let a that makes it scoped to the function. hence, the typeof a outside of the function is indeed undefined (since that area of code is not aware of a).
b, on the other hand, is not declared with let. it's being assigned to the number 0, meaning it's an implicit global variable, hence existing out of the function.
Here when the foo() function executions starts java script interpreter
creates variable a in the scope of f00() function
for variable b the interpreter thinks it is accidentally created so the interpreter creates a global variable b and assigns value 0 eg: (window.b = 0; or b = 0;)
this is because scoping rules here var keyword will be scoped to the immediate variable store (eg: a)
so
type of variable a is undefined as it is in the scope of function foo()
type of variable b is number as it is global variable and is available anywhere in the code
Related
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.
Consider the following 2 functions:
cat = function() {
console.log("Meow");
}
and:
var dog = function() {
console.log("woof")
}
cat() -> "Meow"
dog() -> "Woof
They both work, except that cat is not declared using var. Does this mean that it is globally scoped? I would say that both functions are globally scoped. There is also the syntaxfunction cat(){...}, I guess that is similar to the first style, some sort of implicit variable binding...
Could somebody explain the difference between the styles for declaring functions, if there is any.
Variables
If you don't specify var, let or const it will get globally scoped
If you do specify var, let or const then it will get scoped to the nearest enclosing scope depending on that particular specifier
(var - will get scoped to the nearest enclosing function or global scope if not defined inside of a function)
(let & const - will get scoped to the nearest enclosing block)
Functions
Assigning a function as follows:
var dog = function() {
console.log("woof")
}
Means that the function will not be accessible until the line that it is declared on is reached during execution, i.e. you will only be able to execute this function from after the line on which it was declared.
Whereas declaring a function as follows:
function cat(){...}
Means that it will be moved to the top of the enclosing scope, so you will be able to call it from anywhere within the reachable scope even if it's earlier in code than the line on which you declared it on.
Not using var/let/const makes it implicitly global, which is generally regarded as a bad thing. If you use 'use strict', you'll get an error for any implicit globals. The biggest issue that arises with implicit global variables is that you may not know that you've made a global variable. For example:
(function() {
a = 5;
})();
// a doesn't exist right?
console.log(a); // 5... whoops!
No you don't. you can declare a function like so:
function foo(){
}
Then foo is automatically declared at the appropriate scope.
Its all a matter of scopes. where will the interpreter declare the function. Doing it the way you did it, without var will cause the interpreter to create a global variable automatically, and that is the highest scope possible, meaning that it is accessible everywhere in the code. That is considered a bad thing, since you normally wouldn't want to do that unless it is done intentionally, because of deep reasons which I can go into if you wish.
function foo(){
function bar(){
console.log("bar");
}
bar();
console.log(typeof bar);
}
foo();
bar(); // will throw an error since "bar" does not exist at this scope
Read all about function declaration
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
There is a test to whether a will be undefined or b will be undefined. However what is confusing me is, how are we accessing both a and b inside the console.log, when both are local variables. Does it have to do with the way we defined a function inside a parentheses ? I am new to JavaScript and I am trying to understand how it works.
(function(){
var a = b = 3;
})();
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
From outside the function, your console.log() calls can access b but not a. The variable a is declared in the function by the var statement; the variable b however is made implicitly global by the initialization expression for a.
The var statement is interpreted as if it were written
var a = (b = 3);
The syntax of a var statement is such that the initialization does not define b as a local variable. You can verify that with code like yours, or by adding
"use strict";
right before the var statement and noting that you'll get an error from the implicit global use of b.
Well, in your example, you have a "global" variable b and a local variable a. Only b is visible outside of the function.
When you try to refer to a in your console output, it will refer to a "global" variable a (which is not yet defined).
b is "global" because there is no var statement that defines it. The var on that line only applies to a.
a is a local variable only visible inside of the function (nicely declared with var). However, you can still have variables in other parts of your program that are also called a. Your console output is an example for this. But these are completely separate variables.
Variable a is inside a IIFE, so it won't be visible in a parent scope (it has var keyword behind it).
Variable b is presumed to be from outer scope of the IIFE (it does not have var keyword).
The way typeof operator works, it lets you put any valid variable name for a type test, even undeclared i.e.:
var a = 5;
var b;
console.log(typeof a); // number
console.log(typeof b); // undefined
console.log(typeof totallyNotMentionedAnywhereElse); // undefined
Consider the following code:
var a = 'a';
function b() {
console.log(a);
if (!a) {
var a = 'b';
}
}
b();
Running b() prints undefined to the console. However, if you remove the if statement, or even simply remove the var keyword from the expression within the if statement so that you're redefining the outer a variable, the string a will be printed to the console as expected.
Can anyone explain this? The only cause I can think of is that this is a race condition, and the if statement is running just a tad faster than the console.log.
This is not a race condition - it's a language feature and is working as the designers of the Javascript language intended.
Because of hoisted variable definitions (where all variable definitions within a function scope are hoisted to the top of the function), your code is equivalent to this:
var a = 'a';
function b() {
var a;
console.log(a);
if (!a) {
a = 'b';
}
}
b();
So, the locally declared a hides the globally declared a and initially has a value of undefined until your if statement gives it a value.
You can find lots of discussion about this characteristic of the Javascript language by searching for "Javascript hoisting".
When you use var statement in a function, it will be creating a new variable which is local to that function.
All the variables declared in the function, will be moved to the top of the function, irrespective of the place where they are actually declared. This is called Hoisting.
The hoisted variables will have the value undefined, by default, till they are explicitly assigned a value.
It prints undefined as it is, because of the 3rd point.
In your case, you declared a variable a within the if block. Since the variables are hoisted, the declaration is moved to the top of the function. It has the same name as the outer variable. When you access a in the function, it first looks in the current scope if there is a variable by that name exists. It checks other scopes only if it is not found in local scope. So, the local a shadows the outer a. When you remove the var statement, there is no a in local scope, so the outer a is used. That is why it prints a.