What are new ES6 features ?

Here’s a summary of key features introduced in ES6 (ECMAScript 2015) that significantly enhanced JavaScript development:

1. Classes: Syntactic sugar for creating object blueprints with inheritance and encapsulation.

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log("Hello, my name is", this.name);
  }
}

2. Arrow Functions: Concise syntax for writing functions, often used in callbacks and higher-order functions.

const numbers = [1, 2, 3];
const doubled = numbers.map(number => number * 2); // [2, 4, 6]

3. Let and Const:

  • Block-scoped variables with distinct behaviors for better variable management.
  • let: Redeclarable within the same block.
  • const: Cannot be reassigned after declaration.
if (true) {
  let x = 10; // x is only accessible within this block
}
const PI = 3.14159; // PI cannot be reassigned

4. Template Literals: Multi-line strings and string interpolation using backticks (`).

const name = "Bard";
const message = `Hello, ${name}!`; // "Hello, Bard!"

5. Destructuring: Extracting values from arrays or objects into distinct variables.

const person = { name: "Alice", age: 30 };
const { name, age } = person; // name = "Alice", age = 30

6. Modules: Organizing code into reusable blocks with clear dependencies.

// math.js
export function add(x, y) {
  return x + y;
}

// main.js
import { add } from './math.js';
const result = add(5, 3); // 8

7. Default Parameters: assigning default values to function parameters for flexibility.

function greet(name = "World") {
  console.log("Hello,", name);
}

8. Rest Parameters: Gathering remaining function arguments into an array.

function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

9. Spread Operator: Expanding iterables into individual elements in arrays, objects, or function calls.

const numbers1 = [1, 2, 3];
const numbers2 = [...numbers1, 4, 5]; // [1, 2, 3, 4, 5]
What is the difference between null and undefined?

In JavaScript, both undefined and null represent the absence of a meaningful value, but they are used in slightly different contexts.

Undefined means a variable has been declared but not initialized, or when a function does not return a value. Accessing an object property or array element that does not exist also results in undefined. It is a primitive value.

Null is an assignment value that represents the absence of any object value. It is often used to explicitly indicate that a variable or object property should have no value or no reference to any object. It is also a primitive value.

What is the difference between == and === ?

In JavaScript, == and === are two comparison operators used to compare two values. The main difference between them is that == performs type coercion, while === does not. The == operator compares two values for equality after converting them to a common type. For example, 1 == '1' would return true because the string '1' is converted to the number 1 before comparison. The === operator, on the other hand, compares two values for equality without performing any type coercion. For example, 1 === '1' would return false because the two values are of different types.

=====
Performs type coercionDoes not perform type coercion
Compares two values for equality after converting them to a common typeCompares two values for equality without performing any type coercion
What are different scopes in JavaScript ?

Global Scope:

  • The outermost scope in a JavaScript program.
  • Variables declared outside of any function or block belong to the global scope.
  • Accessible from anywhere in the code, including within functions and blocks.
var globalVar = "I'm in the global scope";

function myFunction() {
  console.log(globalVar); // Accessible here
}

Function Scope:

  • Each function creates its own scope, where variables declared within the function are only accessible within that function.
  • Variables from the outer scope are still accessible within the function, but those within the function are not accessible from outside.
function myFunction() {
  var functionVar = "I'm in the function scope";
  console.log(globalVar); // Accessible here
}

console.log(functionVar); // Error: functionVar is not accessible here

Block Scope:

  • Introduced with let and const in ES6.
  • Variables declared with let or const inside blocks (e.g., if statements, for loops) are only accessible within that block.
  • Provides more granular control over variable visibility and avoids potential conflicts.
if (true) {
  let blockVar = "I'm in the block scope";
}

console.log(blockVar); // Error: blockVar is not accessible here
Difference between var, let and const.
  • var: Function-scoped or global-scoped. If declared outside a function, it’s global; if declared inside a function, it’s scoped to that function. This can lead to unexpected behavior because of implicit global pollution.
  • let: Block-scoped. Only accessible within the block (e.g., if statement, loop) where it’s declared. This provides better control and avoids accidental reassignment in the outer scope.
  • const: Block-scoped and immutable. Can’t be reassigned once declared. Offers clarity and prevents accidental modification of data.
function example() {
  var x = 1;
  let y = 2;
  const z = 3;

  if (true) {
    var x = 4; // same variable as above
    let y = 5; // different variable than above
    const z = 6; // different variable than above
    console.log(x, y, z); // 4 5 6
  }

  console.log(x, y, z); // 4 2 3
}

example();

In the above example, var is function-scoped, so the variable x is the same inside and outside the if block. On the other hand, let and const are block-scoped, so the variables y and z are different inside and outside the if block.

What are tuples ?

tuple is a typed array with a pre-defined length and types for each index. Tuples are useful when you want to store a fixed number of values of different types in an array-like structure. Here is an example of how to define a tuple:

let myTuple = [42, "hello", true];

In the above example, we define a tuple myTuple with three elements of types numberstring, and boolean.

What is hoisting ?

In JavaScript, hoisting is a default behavior of moving all declarations to the top of the current scope (to the top of the current script or the current function). This means that regardless of where variables and functions are declared in the code, they are accessible and can be used before their actual declarations in the execution phase.

However, it’s important to note that only the declarations are hoisted, not the initializations. This means that if you try to access a variable before it has been declared, you will get an undefined value. Here’s an example:

console.log(x); // Output: undefined
var x = 5;

In the above example, the variable x is declared after it is used. However, since the declaration is hoisted to the top of the scope, the output of the console.log() statement is undefined.

It’s also important to note that let and const declarations are hoisted, but not initialized. This means that the block of code is aware of the variable, but it cannot be used until it has been declared. Using a let variable before it is declared will result in a ReferenceError. The variable is in a “temporal dead zone” from the start of the block until it is declared. Here’s an example:

carName = "Volvo";
let carName;
// Output: ReferenceError: Cannot access 'carName' before initialization
What is closure in JavaScript ? How it works ?

In JavaScript, a closure is a function that has access to the outer (enclosing) function’s variables—scope chain—even after the outer function has returned. Closures are created every time a function is created, at function creation time. They rely on lexical scoping, meaning variables are resolved based on where they’re defined in the code, not where they’re called

How it works:

  • Inner function creation: When an inner function is defined within an outer function, it captures the variables in the outer function’s scope at the time of its creation.
  • Outer function completion: Even after the outer function finishes running, the inner function still maintains a reference to those captured variables.
  • Accessing outer variables: The inner function can access and modify those variables, even though they’re not directly in its own scope.

Closures are useful for:

  • Preserving state: Keeping variables accessible even after their original scope has ended.
  • Creating private variables: Encapsulating variables within a function, preventing direct access from outside.
  • Implementing modules: Organizing code into self-contained units with private functions and variables.
  • Implementing patterns like callbacks, modules, and object factories.

Example

function createCounter() {
  let count = 0; // Outer function's variable

  return function() {
    count++; // Inner function can access and modify count
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
  • createCounter creates a closure by returning an inner function that references count.
  • Each time counter() is called, it increments the shared count variable, demonstrating how the closure maintains state.
function outerFunction(outerVariable) {
  return function innerFunction(innerVariable) {
    console.log('Outer variable: ' + outerVariable);
    console.log('Inner variable: ' + innerVariable);
  }
}

const newFunction = outerFunction('hello');
newFunction('world');

This will output:

Outer variable: hello
Inner variable: world
Difference between call, bind and apply.

In JavaScript, call, bind, and apply are methods that can be used to set the value of ‘this’ in a function and invoke it with a specified set of arguments. The main differences between them are:

  • call and apply are used to invoke a function immediately, while bind returns a new function with the this value set, which can be invoked later.
  • call and apply are similar in that they both allow you to specify the this value and pass in arguments to the function. The difference is that call takes the arguments as a comma-separated list, while apply takes them as an array.
  • bind is used to create a new function with the this value set to the provided value. It returns a new function that can be invoked later with the specified arguments.

Here’s an example that demonstrates the difference between call, bind, and apply:

const person = {
  name: 'John',
  age: 30,
  greet: function(greeting) {
    console.log(`${greeting}, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

person.greet('Hello'); // Hello, my name is John and I am 30 years old.

const person2 = {
  name: 'Jane',
  age: 25
};

person.greet.call(person2, 'Hi'); // Hi, my name is Jane and I am 25 years old.
person.greet.apply(person2, ['Hi']); // Hi, my name is Jane and I am 25 years old.

const greetPerson2 = person.greet.bind(person2, 'Hi');
greetPerson2(); // Hi, my name is Jane and I am 25 years old.

In the above example, we have an object person with a greet method that logs a message to the console. We then create a new object person2 with a name and age property. We use call and apply to invoke the greet method on person2, passing in the string 'Hi' as the greeting. Finally, we use bind to create a new function greetPerson2 that has the this value set to person2 and the greeting set to 'Hi'. When we invoke greetPerson2, it logs the message to the console with the correct values.

What are arrow functions ?

They are anonymous functions, which means they are functions without a name and not bound with an identifier. Here is an example of how to use arrow functions in JavaScript:

// Traditional anonymous function
let x = function(x, y) {
  return x * y;
}

// Using arrow functions
let x = (x, y) => x * y;

In the above example, we define a function x that takes two parameters x and y and returns their product. We then define the same function using an arrow function.

Arrow functions have a shorter syntax than traditional functions and can make your code more concise and readable. They also have a lexical this binding, which means that the this value inside an arrow function is the same as the this value outside the function.

What are higher-order functions (HOFs) ?

In JavaScript, higher-order functions (HOFs) are functions that treat other functions as values. They can take functions as arguments, return functions as results, or both. This powerful concept enables flexible and expressive code patterns.

Here’s a breakdown of key concepts:

1. Functions as First-Class Citizens:

  • In JavaScript, functions are first-class citizens, meaning they can be:
    • Assigned to variables
    • Passed as arguments to other functions
    • Returned from functions
    • Stored in data structures

2. HOFs as Arguments:

  • map(): Applies a function to each element of an array, creating a new array with transformed elements:
const numbers = [1, 2, 3];
const doubledNumbers = numbers.map(number => number * 2); // [2, 4, 6]
  • filter(): Creates a new array with elements that pass a test implemented as a function:
const evenNumbers = numbers.filter(number => number % 2 === 0); // [2]
  • reduce(): Reduces an array to a single value by iteratively applying a function:
const sum = numbers.reduce((total, number) => total + number, 0); // 6

3. HOFs as Return Values:

  • setTimeout(): Schedules a function to be executed after a delay:
setTimeout(() => console.log("Hello after 2 seconds"), 2000);
  • createFunction(): Creates a new function with a custom closure:
function createMultiplier(factor) {
  return number => number * factor;
}

const double = createMultiplier(2);
const result = double(5); // 10
Explain about callbackspromises, and async/await.

In JavaScript, callbackspromises, and async/await are used to handle asynchronous operations.

Callbacks are functions that are passed as arguments to other functions and are executed when the other function has finished executing. They are commonly used in Node.js to handle I/O operations and other asynchronous tasks. However, callbacks can lead to callback hell, which is a situation where nested callbacks become difficult to read and maintain.

Promises were introduced in ES6 as a way to handle asynchronous operations in a more readable and maintainable way. A promise is an object that represents the eventual completion (or failure) of an asynchronous operation and allows you to attach callbacks to it. Promises have three states: pendingfulfilled, and rejected. You can use the then() method to attach a callback to a fulfilled promise and the catch() method to attach a callback to a rejected promise.

Async/await is a newer syntax introduced in ES8 that makes it even easier to work with promises. The async keyword is used to define an asynchronous function, and the await keyword is used to wait for a promise to resolve before continuing execution. Async/await makes it possible to write asynchronous code that looks and behaves like synchronous code, which is easier to read and maintain.

Here’s an example that demonstrates the difference between callbacks, promises, and async/await:

// Callbacks
function getData(callback) {
  setTimeout(() => {
    callback('Data received');
  }, 1000);
}

getData((data) => {
  console.log(data);
});

// Promises
function getData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data received');
    }, 1000);
  });
}

getData()
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.log(error);
  });

// Async/await
async function getData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data received');
    }, 1000);
  });
}

async function main() {
  const data = await getData();
  console.log(data);
}

main();

In the above example, we define a function getData() that simulates an asynchronous operation using setTimeout(). We then demonstrate how to use callbacks, promises, and async/await to handle the result of the asynchronous operation.

Difference between promise.all and promise.allSettled.

Both Promise.all and Promise.allSettled are methods in JavaScript that deal with multiple promises, but they differ in their approach to handling successful and failed outcomes:

Promise.all is used to execute multiple promises in parallel and returns a new promise that resolves when all the promises have resolved. If any of the promises reject, the returned promise will reject with the reason of the first promise that rejects. The resolved values of the promises are returned as an array in the order they were passed.

Promise.allSettled is used to execute multiple promises in parallel and returns a new promise that resolves when all the promises have either resolved or rejected. The resolved values of the promises are returned as an array of objects, where each object represents the status of a promise. The object contains the following properties: status (either "fulfilled" or "rejected"), value (the resolved value of the promise, if fulfilled), and reason (the rejection reason of the promise, if rejected)

Here’s an example that demonstrates the difference between Promise.all and Promise.allSettled:

const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values);
  })
  .catch((error) => {
    console.log(error);
  });
// Output: Error

Promise.allSettled([promise1, promise2, promise3])
  .then((results) => {
    console.log(results);
  });
// Output: [{status: "fulfilled", value: 1}, {status: "rejected", reason: "Error"}, {status: "fulfilled", value: 3}]

In the above example, we define three promises: promise1promise2, and promise3promise1 and promise3 resolve with values of 1 and 3, respectively, while promise2 rejects with an error message. When we use Promise.all to execute these promises, the returned promise rejects with the error message of promise2. On the other hand, when we use Promise.allSettled, the returned promise resolves with an array of objects representing the status of each promise

What is the difference between setTimeout() and setInterval() ?

setTimeout() and setInterval() are two methods in JavaScript that allow the user to schedule callbacks in the event loop. The main difference between them is the frequency of the callback execution. setTimeout() schedules a callback function to be executed once after a specified delay in milliseconds. The callback function is executed only once, and the timer is cleared using the clearTimeout() method. setInterval() schedules a callback function to be executed repeatedly at a specified interval in milliseconds. The callback function is executed repeatedly until the timer is cleared using the clearInterval() method.

In general, setTimeout() is used when you want to execute a function once after a specified delay, while setInterval() is used when you want to execute a function repeatedly at a specified interval.

// setTimeout() example
function sayHello() {
  console.log('Hello!');
}
setTimeout(sayHello, 1000); // prints 'Hello!' after 1 second

// setInterval() example
let count = 0;
function incrementCount() {
  count++;
  console.log(count);
}
setInterval(incrementCount, 1000); // prints 1, 2, 3, ... every second

In the above example, setTimeout() is used to print ‘Hello!’ after a delay of 1 second. setInterval() is used to increment the count variable and print it to the console every second.

Define an object and access its properties. Explain prototype inheritance and object creation methods.

In JavaScript, an object is a collection of related data or functionality, consisting of key-value pairs. Objects can be created using object literals, constructor functions, or the Object.create() method.

// Object literal
const person = {
  name: 'John',
  age: 30,
  address: {
    street: '123 Main St',
    city: 'Anytown',
    state: 'CA'
  }
};

// Constructor function
function Person(name, age) {
  this.name = name;
  this.age = age;
}

const john = new Person('John', 30);

// Object.create() method
const obj = Object.create(null);
obj.name = 'John';
obj.age = 30;

In this example, we create an object using an object literal, a constructor function, and the Object.create() method. We also access the properties of the object using dot notation and bracket notation.

Prototype inheritance is a mechanism in JavaScript that allows objects to inherit properties and methods from other objects. Every object in JavaScript has a prototype, which is an object that it inherits properties and methods from. When a property or method is accessed on an object, JavaScript first looks for the property or method on the object itself. If it doesn’t find it, it looks for it on the object’s prototype, and so on up the prototype chain until it finds the property or method or reaches the end of the chain.

// Constructor function
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

const john = new Person('John', 30);
john.greet(); // prints 'Hello, my name is John and I am 30 years old.'

In this example, we define a Person constructor function and add a greet() method to its prototype. We then create a new Person object and call the greet() method on it.

What are different ways to iterate through arrays in JavaScript?

There are several ways to iterate through an array in JavaScript. Here are some of the most common methods:

  1. for loop: You can use a for loop to iterate over an array by using the array’s length property to determine the number of iterations:
const arr = [1, 2, 3, 4, 5];

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}
  1. forEach() method: You can use the forEach() method to iterate over an array by passing a callback function to the method:
const arr = [1, 2, 3, 4, 5];

arr.forEach(function(element) {
  console.log(element);
});
  1. map() method: You can use the map() method to iterate over an array and return a new array with the results of the callback function:
const arr = [1, 2, 3, 4, 5];

const newArr = arr.map(function(element) {
  return element * 2;
});

console.log(newArr);
What are different ways to iterate through objects in JavaScript?

In JavaScript, there are several ways to iterate through an object:

  1. for…in loop: You can use a for...in loop to iterate over an object’s keys:
const obj = { a: 1, b: 2, c: 3 };

for (let key in obj) {
  console.log(key, obj[key]);
}
  1. Object.keys() method: You can use the Object.keys() method to get an array of an object’s keys and then iterate over the array:
const obj = { a: 1, b: 2, c: 3 };

Object.keys(obj).forEach(function(key) {
  console.log(key, obj[key]);
});
  1. Object.values() method: You can use the Object.values() method to get an array of an object’s values and then iterate over the array:
const obj = { a: 1, b: 2, c: 3 };

Object.values(obj).forEach(function(value) {
  console.log(value);
});
  1. Object.entries() method: You can use the Object.entries() method to get an array of an object’s key-value pairs and then iterate over the array:
const obj = { a: 1, b: 2, c: 3 };

Object.entries(obj).forEach(function([key, value]) {
  console.log(key, value);
});
Explain event handling and event listeners.

In JavaScript, event handling is the process of responding to user actions, such as mouse clicks, keyboard presses, and touch events. Event handling is an important part of building interactive web applications, as it allows the application to respond to user input in real-time.

An event listener is a function that listens for a specific event to occur and then executes a block of code in response to that event. Event listeners are added to HTML elements using the addEventListener() method. The addEventListener() method takes two arguments: the type of event to listen for (such as click or keydown) and the function to execute when the event occurs.

const button = document.getElementById('myButton');

button.addEventListener('click', function() {
  console.log('Button clicked!');
});

In this example, we add an event listener to a button element with the ID myButton. The event listener listens for the click event and logs a message to the console when the button is clicked.

What is deep and shallow copy ?

Shallow Copy:

  • Creates a new object with references to the same nested objects and arrays as the original object.
  • Changes to nested objects or arrays in either the original or the copy will affect both.

Methods for Shallow Copy:

  1. Spread Operator (...):
const original = { name: "Bard", skills: ["coding", "writing"] };
const shallowCopy = { ...original };
  1. Object.assign():
const shallowCopy = Object.assign({}, original);

Deep Copy:

  • Creates a new object with independent copies of all nested objects and arrays.
  • Changes to nested objects or arrays in one object will not affect the other.

Methods for Deep Copy:

  1. JSON.parse(JSON.stringify()):
const deepCopy = JSON.parse(JSON.stringify(original));
  1. Custom Recursive Function:
function deepCopy(obj) {
  if (typeof obj !== "object" || obj === null) return obj;
  const copy = Array.isArray(obj) ? [] : {};
  for (const key in obj) {
    copy[key] = deepCopy(obj[key]);
  }
  return copy;
}
// Shallow copy
let obj1 = { a: 1, b: 2 };
let obj2 = Object.assign({}, obj1);

obj2.a = 3;

console.log(obj1); // Output: { a: 1, b: 2 }
console.log(obj2); // Output: { a: 3, b: 2 }

let obj3 = { a: 1, b: { c: 2 } };
let obj4 = Object.assign({}, obj3);

obj4.b.c = 3;

console.log(obj3); // Output: { a: 1, b: { c: 3 } }
console.log(obj4); // Output: { a: 1, b: { c: 3 } }

// Deep copy
let obj5 = { a: 1, b: { c: 2 } };
let obj6 = JSON.parse(JSON.stringify(obj3));

obj5.b.c = 3;

console.log(obj5); // Output: { a: 1, b: { c: 2 } }
console.log(obj6); // Output: { a: 1, b: { c: 3 } }

Leave a Reply

Your email address will not be published. Required fields are marked *