jsnotes

Topic 004: Concept refresher

Topic 004: Concept refresher

1. Redeclaration of variable

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

2. concatenation and unary

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

3. Block, Local and Global scope

In the provided JavaScript code:

function print() {
  var x = 21;
  let y = 20;
  console.log(window.x);
  console.log(window.y);
}
print();
  1. Inside the function print(), two variables are declared: x using var and y using let.
  2. Variable 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.
  3. Variable 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.
  4. The function is called print().
  5. Inside the function, console.log(window.x) will log the value of x attached to the global object (window in a browser), which is 21.
  6. console.log(window.y) will log undefined because y is not attached to the global object due to being declared with let.
  7. Therefore, the output of the function will be:

    21
    undefined
    
{
  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.

4. For loop var vs let

let in for loop:

for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 1000);
}
  1. The for loop initializes a block-scoped variable i with a value of 0.
  2. It checks the condition i < 5.
  3. If the condition is true, it executes the loop body and increments i by 1.
  4. Inside the loop body:
    • setTimeout() is used to schedule the execution of a function after a specified delay (in milliseconds).
    • An arrow function is passed to setTimeout(), which logs the value of i to the console.
    • However, this logging function is not executed immediately; it is scheduled to be executed after a delay of 1000 milliseconds (1 second).
  5. This process repeats until the condition i < 5 becomes false (i.e., after i reaches 5).
  6. Since 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.
  7. As a result, after 1 second, the logged values will be 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.

var in for loop:

In the provided JavaScript code:

for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 1000);
}
  1. The for loop initializes a variable i with a value of 0.
  2. It checks the condition i < 5.
  3. If the condition is true, it executes the loop body and increments i by 1.
  4. Inside the loop body:
    • setTimeout() is used to schedule the execution of a function after a specified delay (in milliseconds).
    • An arrow function is passed to setTimeout(), which logs the value of i to the console.
    • However, this logging function is not executed immediately; it is scheduled to be executed after a delay of 1000 milliseconds (1 second).
  5. This process repeats until the condition i < 5 becomes false (i.e., after i reaches 5).
  6. When the loop finishes executing, the value of i will be 5.

Now, let’s discuss the behavior of setTimeout() inside the loop:

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.

var + closure = let

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:

This way, we print 1 2 3 4 5 to the console using only var.

5. Assign value then declare concept in JS

In the below JavaScript code:

x = 10;
console.log(x);
var x;
  1. The variable 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.
  2. When the code is executed, x is assigned the value 10.
  3. Then, console.log(x) logs the value of x, which is 10.
  4. After that, 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.
  5. Therefore, the output of the code will be:
    10
    

In the below JavaScript code:

x = 10;
y = 10;
console.log(x);
console.log(y);
const y;
let x;
  1. 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.
  2. 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.

  3. console.log(x);: This prints the value of x to the console. The value of x is 10.

  4. console.log(y);: This prints the value of y to the console. The value of y is also 10.

  5. 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.

  6. 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.

6. Advantages of Arrow functions

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.