Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Mohamed-Code-309/12163e62bfd0bbef1a8c78c63d7c7924 to your computer and use it in GitHub Desktop.
Save Mohamed-Code-309/12163e62bfd0bbef1a8c78c63d7c7924 to your computer and use it in GitHub Desktop.

Top 13 Javascript Interview Questions You Should Answer !:scream_cat:

undefined and null are two special values in JavaScript that represent the absence of a value. However, there is a difference between them in both defination and type:

  • undefined is a value that is automatically assigned to a variable that has been declared but has not been assigned a value, , on the other hand, null is a value that can be explicitly assigned to a variable to represent the absence of a value.
  • undefined has a type of undefined, while null has a type of object.
var x;
console.log(x); // Output: undefined

var y = null;
console.log(y); // Output: null

console.log(typeof x); // Output: undefined
console.log(typeof y); // Output: object

MOVE TO TOP ☝️

var, let, and const are all used to declare variables, but they have some differences in terms of how they behave and the scope they have:

1.var:

  • Variables declared with var can be re-declared and updated anywhere in the program.
function example() {
  var x = 10;
  if (true) {
    var x = 20; // this will overwrite the previous declaration of x
    console.log(x); // output: 20
  }
  console.log(x); // output: 20
}
  • Varibles declared with var are hoisted. Hoisting is moving declarations to the top of the current scope.
x = 5               //intilize x (assign value to x) 
console.log(x);     //output: 5
var x;              //declaring x with var

//the previous code is similar to:
var y;              // declare y with var
y = 5;              // intilize y (assign value to y)
console.log(y);     //output: 5     
  • var is a function-scoped, variables declared with var inside a function can be used anywhere witin a function.
function sayHello() {
  a = 'hello';
  console.log(a);
  var a;
}

//'a' is hoisted to the top of the function and become local variable
sayHello();         //output: 'hello' 

console.log(a);  //ERROR: a is not defined cause
//'a' is only accessible inside the function and doesn't become a global variable cause it is declared with var
  • Redeclaring a varible with var in a block scope will change the value too in the outer scope. A block is any code surrounded by curly braces { }.
var c = 5;
console.log(c);      //output: 5
{
    var c = 10;
    console.log(c);  //output: 10
}
console.log(c);      //output: 10
  • When a variable declared with var in a loop, the value of that variable is changed.
for (var y = 0; y < 3; y++) {
    //processing some data
}
console.log(y); //output: 3

2.let:

  • Variables declared with let can be updated but not re-declared.
function example() {
  let x = 10;
  if (true) {
    let x = 20; // this will create a new variable x that only exists inside the block
    console.log(x); // output: 20
  }
  console.log(x); // output: 10
}
  • Varibles declared with let are not hoisted. In other words cannot be used untill they are declared.
x = 5               //intilize x (assign value to x) 
console.log(x);     //ERROR: Cannot access 'x' before initialization 
let x;              //declaring x with let
  • let is block-scoped, which means that if you declare a variable with let inside a block, it's only accessible within that block.
let c = 5;
console.log(c);     //output: 5
{
    let c = 10;
    console.log(c); //output: 10
}
console.log(c);     //output: 5
  • When a variable declared with let in a loop, the value of that variable doesn't change cause let is blocked scope.
let y = 100;
for (let y = 0; y < 3; y++) {
  //processing some data
}
console.log(y); //output: 100

3.const:

  • Like let, const is also block-scoped.
  • Variables declared with const cannot be re-declared or updated.
const x = 100;
x = 200;        //ERROR: Assignment to constant variable.
console.log(x);

MOVE TO TOP ☝️

forEach() is used to execute a provided function once for each element in an array, in order. It doesn't return anything and simply iterates over the array. Here's an example:

const numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => console.log(num));

In this example, forEach() is used to iterate over the numbers array and log each element to the console.

map(), on the other hand, is used to create a new array with the results of calling a provided function on every element in the original array. It returns a new array with the same length as the original array, but with each element transformed by the provided function. Here's an example:

const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(num => num * num);

console.log(squaredNumbers); // Output: [1, 4, 9, 16, 25]

The forEach() method returns undefined always, regardless of what the callback function returns.

const numbers = [1, 2, 3];
const nums1 = numbers.forEach(num => num * 2); //undefined
const nums2 = numbers.map(num => num * 2);  //[2, 4, 6]

Use forEach() when you want to execute a function for each element in an array without modifying the array. Use map() when you want to create a new array with the results of calling a function on each element of the original array.

MOVE TO TOP ☝️

for...in and for...of are two different types of loops used to iterate over data structures such as objects and arrays.

for...in: this loop is used to iterate over the properties of an object. It's typically used to loop over object keys.

const obj = {a: 1, b: 2, c: 3};
for (let prop in obj) {
  console.log(prop + ": " + obj[prop]); 
}
/*
output:
a: 1
b: 2
c: 3
*/

for...of: this loop is used to iterate over the values of an array or other iterable object such as string or set, and it doesn't work with objects.

const arr = [1, 2, 3];
for (let value of arr) {
  console.log(value);
}
/*
output:
1
2
3
*/

MOVE TO TOP ☝️

find is used to find the first element in an array that matches a given condition, and returns that element. If no element matches the condition, find returns undefined.

const numbers = [1, 2, 3, 4, 5];
const result = numbers.find(num => num > 3);

console.log(result); // Output: 4

filter, on the other hand, is used to create a new array containing all elements that match a given condition. if no element matches the condition, filter will return an empty array.

const numbers = [1, 2, 3, 4, 5];
const result = numbers.filter(num => num > 3);

console.log(result); // Output: [4, 5]

MOVE TO TOP ☝️

1.call:

The call method is used to invoke a function with a given this value and arguments provided individually. It accepts the this context as its first argument, followed by arguments as comma-separated values if they are existed.

example 1:

function add(a, b) {
  return a + b + this.value;
}

const obj = { value: 10 };
const result = add.call(obj, 5, 2); // passing `this` as `obj` and `5` and `2` as arguments
console.log(result); // output: 17

example 2:

const employee1 = {
  name: "ahmed",
  salary: 4000,
  calcSalaryWithBonus: function () {
      this.salary = this.salary + 500;
      console.log(`${this.name}'s salary after bonus is ${this.salary}`);
  }
}

employee1.calcSalaryWithBonus();  //output: ahmed's salary after bonus is 4500

const employee2 = {
  name: "Aya",
  salary: 3000
}


//we want to call calcSalaryWithBonus in the context of emplyee2
//in other word reuse calcSalaryWithBonus for other object that doesn't have this method
employee1.calcSalaryWithBonus.call(employee2); //output: Aya's salary after bonus is 3500

2.apply:

The apply method is similar to call but takes arguments as an array. It is used to invoke a function with a given this value and an array of arguments.

example 1:

function add(a, b) {
  return a + b + this.value;
}

const obj = { value: 10 };
const result = add.apply(obj, [5, 2]); // passing `this` as `obj` and `[5, 2]` as arguments array
console.log(result); // output: 17

example 2:

function calcSalaryWithBonus(bonus) {
  this.salary = this.salary + bonus;
  console.log(`${this.name}'s salary after bonus is ${this.salary}`);
}


const employee = {
  name: "Mohamed",
  salary: 7000
}

calcSalaryWithBonus.apply(employee, [500]); //output: Mohamed's salary after bonus is 7500

3.bind:

The bind method is used to create a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

example 1:

function add(a, b) {
  return a + b + this.value;
}

const obj = { value: 10 };
const addWithObj = add.bind(obj); // creating a new function with `this` bound to `obj`
const result = addWithObj(5, 2); // calling the new function with arguments `5` and `2`
console.log(result); // output: 17

example 2:

function calcSalaryWithBonus(bonus) {
  this.salary = this.salary + bonus;
  console.log(`${this.name}'s salary after bonus is ${this.salary}`);
}


const employee = {
  name: "Mohamed",
  salary: 7000
}

const calSalFunc = calcSalaryWithBonus.bind(employee, 500);

calSalFunc(); //output: Mohamed's salary after bonus is 7500
calSalFunc(); //output: Mohamed's salary after bonus is 8000
calSalFunc(); //output: Mohamed's salary after bonus is 8500
console.log(employee.salary); //output: 8500

MOVE TO TOP ☝️

shallow and deep copying can occur with objects and arrays, because both are reference types that are stored as references to their location in memory.

Shallow Copy

Shallow Copy: means that only the first level of the object/array is copied, while any deeper levels of the object/array are still referenced by the original object/array.

Object Shallow Copy:

A shallow copy of an object can be achieved using the spread operator (…) or using Object.assign() method.

Here's an example that demonstrates how a shallow copy works in objects:

const obj1 = {
  name: 'ahmed',
  age: 33,
  hobbies: ['reading', 'cooking', 'hiking']
};

//const obj2 = {...obj1}
const obj2 = Object.assign({}, obj1);

obj2.hobbies.push('coding');

console.log(obj1); // { name: 'John', age: 30, hobbies: ['reading', 'cooking', 'hiking', 'coding'] }
console.log(obj2); // { name: 'John', age: 30, hobbies: ['reading', 'cooking', 'hiking', 'coding'] }

In this example, obj1 is an object with three properties: name, age, and hobbies. obj2 is a shallow copy of obj1 created using Object.assign(). When we modify the hobbies property of obj2 by pushing a new hobby, that modification is reflected in both obj1 and obj2. This is because obj2.hobbies is still referencing the same memory location as obj1.hobbies.

Array Shallow Copy:

You can create a shallow copy of an array using the spreed operator(...) and the slice(), Array.from() and concat() methods.

Here's an example that demonstrates how a shallow copy works with an array:

const arr1 = [1, 2, [3, 4]];

const arr2 = arr1.slice();

arr2[2].push(5);

console.log(arr1); // [1, 2, [3, 4, 5]]
console.log(arr2); // [1, 2, [3, 4, 5]]

Whenever the original array contains objects or arrays, those objects or arrays will still be referenced by both the original array and the copy, because the copy is only a shallow copy.

Deep Copy

Deep copy: means that all levels of the array or object are copied. This is a true copy of the object. A deep copy creates a completely new object/array with all properties and values copied to new memory locations.

If you want to create a deep copy of an object/array, you can use JSON.parse(JSON.stringify()).

Object Deep Copy

// Deep copy of an object
const obj1 = { 
  name: 'ahmed', 
  age: 30, 
  address: { 
    city: 'Cairo', 
    country: 'Egypt' 
  } 
};

const obj2 = JSON.parse(JSON.stringify(obj1));

obj2.address.city = 'Alex';

console.log(obj1.address.city); // 'Cairo'
console.log(obj2.address.city); // 'Alex'

Array Deep Copy

// Deep copy of an array of objects
const arr1 = [
  { name: 'Wafaa', age: 30 },
  { name: 'Ahmed', age: 25 },
  { name: 'Hend', age: 35 }
];

const arr2 = JSON.parse(JSON.stringify(arr1));

arr2[0].age = 40;

console.log(arr1[0].age); // 30
console.log(arr2[0].age); // 40

MOVE TO TOP ☝️

== and === are comparison operators that are used to compare two values for equality. However, they differ in how they perform the comparison:

The == performs a loose comparison, also known as type coercion. It compares the values for equality after converting their data types if necessary. For example, 5 == '5' would return true, because the string '5' is converted to the number 5 before the comparison is made. However, this can sometimes lead to unexpected results or or subtle bugs in your code.

The === operator performs a strict comparison, also known as type checking. It compares the values for equality without converting their data types. For example, 5 === '5' would return false, because the two values have different data types.

Using === is generally considered to be safer and more reliable than using ==, because it avoids the potential pitfalls of type coercion.

MOVE TO TOP ☝️

parseInt and Number are both built-in functions in JavaScript that convert a given value to a number.

parseInt : If the string does not represent a valid integer, parseInt returns NaN (Not a Number).

var age = "30";
var intAge = parseInt(age);
console.log(intAge); // 30

console.log(parseInt("Hello"));    // Output: NaN
console.log(parseInt(true));    // Output: NaN
console.log(parseInt(false));   // Output: NaN
console.log(parseInt({}));      // Output: NaN

Number It can also be used as a function to convert strings or other values to numbers and can be used to convert values in other number systems (such as binary and hexadecimal) to numbers.

console.log(Number("30"));  // Output: 1050

// Using Number to convert non-string values
// Converting a boolean to a number
console.log(Number(true));  // Output: 1
console.log(Number(false)); // Output: 0

// Converting an object to a number
console.log(Number({}));      // Output: NaN (Not a Number)
console.log(Number("Hello")); // Output: NaN (Not a Number)
// convert a binary string to a number
var binaryStr = "1010";
var num = Number.parseInt(binaryStr, 2);
console.log(num); //Output: 10


// convert a hexadecimal string to a number
var hexStr = "FF";
var num = Number.parseInt(hexStr, 16);
console.log(num); //Output: 255

When working with decimal numbers, parseInt only returns the integer part of the decimal number, discarding any decimal part. Number, on the other hand, returns the decimal number as is.

// Using parseInt to convert a decimal number to an integer
console.log(parseInt(3.14));  // Output: 3
console.log(parseInt(3.99));  // Output: 3

// Using Number to convert a decimal number to a number
console.log(Number(3.14));    // Output: 3.14
console.log(Number(3.99));    // Output: 3.99

MOVE TO TOP ☝️

Arrow functions and normal functions are both used in JavaScript to define and execute functions, but they differ in their syntax and behavior

Syntax:

Normal functions have a more verbose syntax, which is much longer and often using more characters, symbols, and keywords than necessary.

Arrow functions have a more concise syntax, which is a shorter and more readable way of writing code.

// normal function
function add(a, b) {
  return a + b;
}

// arrow function
const add = (a, b) => a + b;

Behavior with this keyword:

Normal functions When a regular method is called on an object, the this keyword inside the method is automatically set to the object that the method is called on.

const person = {
    name: "ahmed",
    age: 30,
    sayHi: function () {
        console.log(`Hi, my name is ${this.name}`)
    }
};

person.sayHi(); //Hi, my name is ahmed

sayHi method is defined as a regular function within the person object, and regular functions in JavaScript are automatically bound to the object they are called on.

Arrow functions don't create their own scope and don't bind this to that scope. Instead, they use the this value of their parent scope

  • example 1:
const person = {
    name: "ahmed",
    age: 30,
    sayHi: () => {
        console.log(`Hi, my name is ${this.name}`) 
    }
};
person.sayHi(); //Hi, my name is undefined

in the previous example, the arrow function is defined inside the person object, but this inside the arrow function refers to the global object, not the person object.

The this value refer to the enclosing scope where the arrow function is defined, not the object that the method is a property of.

  • example 2:
const person = {
  name: 'ahmed',
  sayHi: function() {
    setTimeout(() => {
      console.log(`Hi, my name is ${this.name}`);
    }, 1000);
  }
};
  
person.sayHi() //"Hi, my name is ahmed" after 1 second

In the above example, this.name is correctly referring to the name property of the person object, because the arrow function used with setTimeout is defined inside the sayHi method.

In contrast, in the first example, the arrow function is defined at the top level and is not part of any object or method. Therefore, this inside the arrow function refers to the global object, which does not have a name property, resulting in undefined.

MOVE TO TOP ☝️

A pure function is a function that always returns the same result if the same arguments(input) are passed in the function, and does not cause any side effects. In other words, a pure function has no external dependencies, no side effects, and does not modify its inputs.

An impure function, on the other hand, is a function that does not satisfy these criteria. It can return different results for the same inputs, cause side effects such as modifying global variables or making API calls, or depend on external state.

example to demonstrate the difference between a pure and an impure function:

// Pure function
function add(a, b) {
  return a + b;
}

// Impure function
let counter = 0;
function incrementCounter() {
  counter++;
}

the add function is pure because it always returns the same result for the same inputs and does not cause any side effects. The incrementCounter function is impure because it modifies a global variable counter, causing a side effect.

MOVE TO TOP ☝️

setTimeout is a JavaScript function that sets a timer to execute a function or code snippet after a specified amount of time has passed. For example:

setTimeout(function(){ console.log("Hello, World!"); }, 1000);

The previous code sets a timer to execute the function console.log("Hello, World!") after 1,000 milliseconds (or 1 second) have passed.

setInterval is similar to setTimeout, but it repeatedly executes the function or code snippet at a specified interval of time. For example:

setInterval(function(){ console.log("Hello, World!"); }, 1000);

the previous code sets a timer to execute the function console.log("Hello, World!") every 1,000 milliseconds (or 1 second) indefinitely.

MOVE TO TOP ☝️

The spread operator (...) is used to spread the elements of an iterable object (like an array or a string) into separate elements or arguments. It allows you to:

  1. concatenate multiple arrays into a single array (the same action can be done with objects).
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2];
console.log(arr3); // Output: [1, 2, 3, 4, 5, 6]
  1. spread an array into a list of elements when calling a function.
  function sum(a, b, c) {
  return a + b + c;
  }

  const numbers = [1, 2, 3];
  console.log(sum(...numbers)); // Output: 6

The rest parameter (...) syntax allows a function to accept an infinite number of arguments as an array. The rest parameter must be the last parameter in a function.

function sum(...args) {
  return args.reduce((acc, cur) => acc + cur, 0);
}

console.log(sum(1, 2, 3));        // Output: 6
console.log(sum(1, 2, 3, 4, 5));  // Output: 15

MOVE TO TOP ☝️

Related Content:

✔️ Variable Scoping in Javascript (coming soon)
✔️ Looping Methods in Javacript (coming soon)
✔️ Mutated vs Non-Mutated Array Methods
✔️ Soft Binding vs Hard Binding in javascript (coming soon)
✔️ The Magic of Spreed Operator (...) in Javacript (coming soon)

Links:

🕴️ Linkedin: Dragon Slayer 🐲
📝 Articles: All Articles written by D.S

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment