Topic 004: Concept refresher
let a = 3;
console.log(a);
let a = 5;
console.log(a);
In JavaScript, you cannot declare the same variable twice using let
within the same scope. If you try to do so, it will result in a syntax error. So, the code you provided will throw an error at the second let a = 5
declaration.
To fix this, you can either change the second let a = 5
to just a = 5
, or if you want to redeclare a
, you would use var
or const
for the first declaration and let
for the second. Here’s the corrected version:
let a = 3;
console.log(a);
a = 5; // Reassigning the value of 'a'
console.log(a);
This will output:
3
5
console.log(3 + "3")
: This will output "33"
. When you use the +
operator with a number and a string, JavaScript coerces the number into a string and performs string concatenation.
console.log(3 - "3")
: This will output 0
. JavaScript tries to perform arithmetic operations, so it will try to convert the string "3"
into a number and subtract it from 3
, resulting in 0
.
console.log(+"3" + 3)
: This will output 6
. The unary plus operator (+
) converts the string "3"
into a number, and then adds 3
to it, resulting in 6
.
console.log("3" == 3)
: This will output true
. The loose equality operator (==
) performs type coercion, so it converts the string "3"
into a number before comparing, which results in true
because 3
is equal to 3
.
console.log("3" === 3)
: This will output false
. The strict equality operator (===
) does not perform type coercion, so it compares the string "3"
with the number 3
directly, resulting in false
because they are of different types.
console.log(1 < 2 < 3)
: This will output true
. This expression is evaluated left-to-right. 1 < 2
evaluates to true
, which is converted to 1
in numerical comparison, so 1 < 3
also evaluates to true
.
console.log(3 > 2 > 1)
: This will output false
. This expression is also evaluated left-to-right. 3 > 2
evaluates to true
, which is converted to 1
in numerical comparison. Then 1 > 1
evaluates to false
.
console.log(isNaN('12'))
: This will output false
. The isNaN()
function checks if a value is “Not-a-Number”. However, the string '12'
can be coerced into a number, so it’s not NaN, resulting in false
.
In the provided JavaScript code:
function print() {
var x = 21;
let y = 20;
console.log(window.x);
console.log(window.y);
}
print();
print()
, two variables are declared: x
using var
and y
using let
.x
is declared using var
, which means it becomes a property of the global object (window
object in browsers) because it’s declared outside any function or block scope. So, window.x
will refer to the variable x
declared using var
.y
is declared using let
, which means it’s block-scoped to the print()
function and not attached to the global object. Therefore, window.y
will be undefined
.print()
.console.log(window.x)
will log the value of x
attached to the global object (window
in a browser), which is 21
.console.log(window.y)
will log undefined
because y
is not attached to the global object due to being declared with let
.Therefore, the output of the function will be:
21
undefined
In JavaScript, scope refers to the accessibility of variables. There are three types of scopes: block scope, local scope, and global scope. Let me explain each with code examples:
let
and const
keywords, block scope confines variables within the nearest curly braces {}
. Variables declared with let
or const
inside a block are only accessible within that block.{
let x = 10; // block-scoped variable
console.log(x); // Output: 10
}
console.log(x); // Error: x is not defined
function myFunction() {
var y = 20; // local variable
console.log(y); // Output: 20
}
myFunction();
console.log(y); // Error: y is not defined
var z = 30; // global variable
function anotherFunction() {
console.log(z); // Output: 30
}
console.log(z); // Output: 30
var globalVariable = 10; // Global variable
function print() {
var localVariable = 21; // Local variable to the function
{
let blockVariable = 22; // Block-scoped variable
console.log("Inside block:", blockVariable); // Accessible inside the block
}
console.log("Inside function - localVariable:", localVariable); // Accessible inside the function
console.log("Inside function - globalVariable:", globalVariable); // Accessible inside the function
console.log("Inside function - window.globalVariable:", window.globalVariable); // Accessible inside the function via window object
}
print();
console.log("Outside function - globalVariable:", globalVariable); // Accessible globally
Remember, accessing variables in broader scopes (e.g., global from within a function) is allowed, but accessing variables in narrower scopes (e.g., local from outside its function) will result in an error.
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}
for
loop initializes a block-scoped variable i
with a value of 0
.i < 5
.true
, it executes the loop body and increments i
by 1
.setTimeout()
is used to schedule the execution of a function after a specified delay (in milliseconds).setTimeout()
, which logs the value of i
to the console.1000
milliseconds (1 second).i < 5
becomes false
(i.e., after i
reaches 5
).i
is declared using let
, it’s block-scoped, meaning a new lexical environment is created for each iteration of the loop. This ensures that each invocation of the arrow function inside setTimeout()
captures the value of i
at the time of its execution.0
, 1
, 2
, 3
, and 4
, respectively, as expected. This is because each scheduled execution of the arrow function inside setTimeout()
captures the value of i
at the time of its iteration.In the provided JavaScript code:
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}
for
loop initializes a variable i
with a value of 0
.i < 5
.true
, it executes the loop body and increments i
by 1
.setTimeout()
is used to schedule the execution of a function after a specified delay (in milliseconds).setTimeout()
, which logs the value of i
to the console.1000
milliseconds (1 second).i < 5
becomes false
(i.e., after i
reaches 5
).i
will be 5
.Now, let’s discuss the behavior of setTimeout()
inside the loop:
setTimeout()
is called multiple times within the loop, the functions passed to setTimeout()
are not executed immediately. Instead, they are executed asynchronously after the specified delay.setTimeout()
, by the time the functions passed to it are executed (after a delay of 1 second), the loop has already completed, and the value of i
has become 5
.i
by closure, it will print the value of i
at the time of execution, which is 5
for all the scheduled executions.So, when this code runs, after a delay of 1 second, it will log 5
to the console five times. This is because by the time the logging functions are executed, the loop has already finished executing and the value of i
is 5
.
Using a closure inside the loop to capture the value of i
at each iteration. Here’s how you can print 0
, 1
, 2
, 3
, and 4
instead of five times 5
using var
:
for (var i = 1; i < 5; i++) {
(function (index) {
setTimeout(function () {
console.log(index);
}, 1000 * index);
})(i);
}
In this code:
for
loop to iterate from 1
to 5
.i
as an argument and captures its value in the parameter index
.index
to setTimeout()
to schedule the logging of index
after a delay. Since each setTimeout()
call captures the value of index
at the time it’s invoked, it prints 1
after 1 second, 2
after 2 seconds, and so on, until 5
after 5 seconds.This way, we print 1 2 3 4 5
to the console using only var
.
In the below JavaScript code:
x = 10;
console.log(x);
var x;
x
is assigned the value 10
without being declared using var
, let
, or const
. In JavaScript, this is allowed due to the concept of variable hoisting. Hoisting means that variable declarations are moved to the top of their containing scope during the compilation phase, although their assignments remain in place.x
is assigned the value 10
.console.log(x)
logs the value of x
, which is 10
.var x;
is encountered. Despite being declared later in the code, the declaration of x
with var
is hoisted to the top of its scope. However, since x
has already been assigned a value (10
), this declaration doesn’t affect the value of x
.10
In the below JavaScript code:
x = 10;
y = 10;
console.log(x);
console.log(y);
const y;
let x;
x = 10;
: This assigns the value 10
to the variable x
. Since x
is not declared using var
, let
, or const
, it becomes a global variable.y = 10;
: This assigns the value 10
to the variable y
. Similar to x
, since y
is not declared using var
, let
, or const
, it also becomes a global variable.
console.log(x);
: This prints the value of x
to the console. The value of x
is 10
.
console.log(y);
: This prints the value of y
to the console. The value of y
is also 10
.
const y;
: This line attempts to declare the variable y
using const
. However, since y
has already been assigned a value (10
) before its declaration, this will result in a syntax error. In JavaScript, const
requires an initialization at the point of declaration and cannot be assigned a value later.
let x;
: This line declares the variable x
using let
. However, since x
has already been assigned a value (10
) before its declaration, this will not result in an error. But declaring x
here will not have any effect on the previously assigned value of x
.Therefore, the code will throw a syntax error at the line const y;
due to the attempt to declare a const
variable without initialization, and it will output 10
for both x
and y
before encountering the error.
1. Concise Syntax: Arrow functions have a compact and concise syntax, making the code more readable and reducing the amount of boilerplate code. They are particularly useful for writing shorter and more expressive functions.
2. Lexical this Binding: Arrow functions do not have their own this value. Instead, they lexically bind the this value of the enclosing scope. This means that the this value inside an arrow function is automatically inherited from the surrounding context. It eliminates the need to use bind(), call(), or apply() to preserve the this value or deal with this-related issues.
3. No Arguments Object: Arrow functions do not have their own arguments object. Instead, they inherit the arguments object from the enclosing scope. This can be beneficial in scenarios where you need to access the arguments passed to an enclosing function.
4. Implicit Return: Arrow functions provide implicit return behavior for concise one-line functions. If the function body consists of a single expression, you can omit the curly braces and the return keyword. The result of the expression will be automatically returned.
5. Well-suited for Callbacks: Arrow functions are well-suited for callback functions, such as event handlers or asynchronous operations, where the lexical binding of this and the concise syntax can make the code more readable and maintainable.