Skip to content

Latest commit

 

History

History
1251 lines (906 loc) · 50.3 KB

File metadata and controls

1251 lines (906 loc) · 50.3 KB

Table of Contents

No. Questions

| 401 | What is RxJS | | 402 | What is the difference between Function constructor and function declaration | | 403 | What is a Short circuit condition | | 404 | What is the easiest way to resize an array | | 405 | What is an observable | | 406 | What is the difference between function and class declarations | | 407 | What is an async function | | 408 | How do you prevent promises swallowing errors | | 409 | What is deno | | 410 | How do you make an object iterable in javascript | | 411 | What is a Proper Tail Call | | 412 | How do you check an object is a promise or not | | 413 | How to detect if a function is called as constructor | | 414 | What are the differences between arguments object and rest parameter | | 415 | What are the differences between spread operator and rest parameter | | 416 | What are the different kinds of generators | | 417 | What are the built-in iterables | | 418 | What are the differences between for...of and for...in statements | | 419 | How do you define instance and non-instance properties | | 420 | What is the difference between isNaN and Number.isNaN? | | 421 | How to invoke an IIFE without any extra brackets? | | 422 | Is that possible to use expressions in switch cases? | | 423 | What is the easiest way to ignore promise errors? | | 424 | How do style the console output using CSS? | | 425 | What is nullish coalescing operator (??)? | | 426 | How do you group and nest console output? | | 427 | What is the difference between dense and sparse arrays? | | 428 | What are the different ways to create sparse arrays? | | 429 | What is the difference between setTimeout, setImmediate and process.nextTick? | | 430 | How do you reverse an array without modifying original array? | | 431 | How do you create custom HTML element? | | 432 | What is global execution context? | | 433 | What is function execution context? | | 434 | What is debouncing? | | 435 | What is throttling? | | 436 | What is optional chaining? | | 437 | What is an environment record? | | 438 | How to verify if a variable is an array? | | 439 | What is pass by value and pass by reference? | | 440 | What are the differences between primitives and non-primitives? | | 441 | What are hidden classes? | | 442 | What is inline caching? | | 443 | How do you create your own bind method using either call or apply method? | | 444 | What are the differences between pure and impure functions?
| 445 | What is referential transparency? | | 446 | What are the possible side-effects in javascript? | | 447 | What are compose and pipe functions? | | 448 | What is module pattern? | | 449 | What is Functon Composition? | | 450 | How to use await outside of async function prior to ES2022? |

  1. What is RxJS

    RxJS (Reactive Extensions for JavaScript) is a library for implementing reactive programming using observables that makes it easier to compose asynchronous or callback-based code. It also provides utility functions for creating and working with observables.

    ⬆ Back to Top

  2. What is the difference between Function constructor and function declaration

    The functions which are created with Function constructor do not create closures to their creation contexts but they are always created in the global scope. i.e, the function can access its own local variables and global scope variables only. Whereas function declarations can access outer function variables(closures) too.

    Let's see this difference with an example,

    Function Constructor:

    var a = 100;
    function createFunction() {
      var a = 200;
      return new Function("return a;");
    }
    console.log(createFunction()()); // 100

    Function declaration:

    var a = 100;
    function createFunction() {
      var a = 200;
      return function func() {
        return a;
      };
    }
    console.log(createFunction()()); // 200

    ⬆ Back to Top

  3. What is a Short circuit condition

    Short circuit conditions are meant for condensed way of writing simple if statements. Let's demonstrate the scenario using an example. If you would like to login to a portal with an authentication condition, the expression would be as below,

    if (authenticate) {
      loginToPorta();
    }

    Since the javascript logical operators evaluated from left to right, the above expression can be simplified using && logical operator

    authenticate && loginToPorta();

    ⬆ Back to Top

  4. What is the easiest way to resize an array

    The length property of an array is useful to resize or empty an array quickly. Let's apply length property on number array to resize the number of elements from 5 to 2,

    var array = [1, 2, 3, 4, 5];
    console.log(array.length); // 5
    
    array.length = 2;
    console.log(array.length); // 2
    console.log(array); // [1,2]

    and the array can be emptied too

    var array = [1, 2, 3, 4, 5];
    array.length = 0;
    console.log(array.length); // 0
    console.log(array); // []

    ⬆ Back to Top

  5. What is an observable

    An Observable is basically a function that can return a stream of values either synchronously or asynchronously to an observer over time. The consumer can get the value by calling subscribe() method. Let's look at a simple example of an Observable

    import { Observable } from "rxjs";
    
    const observable = new Observable((observer) => {
      setTimeout(() => {
        observer.next("Message from a Observable!");
      }, 3000);
    });
    
    observable.subscribe((value) => console.log(value));

    Screenshot

    Note: Observables are not part of the JavaScript language yet but they are being proposed to be added to the language

    ⬆ Back to Top

  6. What is the difference between function and class declarations

    The main difference between function declarations and class declarations is hoisting. The function declarations are hoisted but not class declarations.

    Classes:

    const user = new User(); // ReferenceError
    
    class User {}

    Constructor Function:

    const user = new User(); // No error
    
    function User() {}

    ⬆ Back to Top

  7. What is an async function

    An async function is a function declared with the async keyword which enables asynchronous, promise-based behavior to be written in a cleaner style by avoiding promise chains. These functions can contain zero or more await expressions.

    Let's take a below async function example,

    async function logger() {
      let data = await fetch("http://someapi.com/users"); // pause until fetch returns
      console.log(data);
    }
    logger();

    It is basically syntax sugar over ES2015 promises and generators.

    ⬆ Back to Top

  8. How do you prevent promises swallowing errors

    While using asynchronous code, JavaScript’s ES6 promises can make your life a lot easier without having callback pyramids and error handling on every second line. But Promises have some pitfalls and the biggest one is swallowing errors by default.

    Let's say you expect to print an error to the console for all the below cases,

    Promise.resolve("promised value").then(function () {
      throw new Error("error");
    });
    
    Promise.reject("error value").catch(function () {
      throw new Error("error");
    });
    
    new Promise(function (resolve, reject) {
      throw new Error("error");
    });

    But there are many modern JavaScript environments that won't print any errors. You can fix this problem in different ways,

    1. Add catch block at the end of each chain: You can add catch block to the end of each of your promise chains

      Promise.resolve("promised value")
        .then(function () {
          throw new Error("error");
        })
        .catch(function (error) {
          console.error(error.stack);
        });

      But it is quite difficult to type for each promise chain and verbose too.

    2. Add done method: You can replace first solution's then and catch blocks with done method

      Promise.resolve("promised value").done(function () {
        throw new Error("error");
      });

      Let's say you want to fetch data using HTTP and later perform processing on the resulting data asynchronously. You can write done block as below,

      getDataFromHttp()
        .then(function (result) {
          return processDataAsync(result);
        })
        .done(function (processed) {
          displayData(processed);
        });

      In future, if the processing library API changed to synchronous then you can remove done block as below,

      getDataFromHttp().then(function (result) {
        return displayData(processDataAsync(result));
      });

      and then you forgot to add done block to then block leads to silent errors.

    3. Extend ES6 Promises by Bluebird: Bluebird extends the ES6 Promises API to avoid the issue in the second solution. This library has a “default” onRejection handler which will print all errors from rejected Promises to stderr. After installation, you can process unhandled rejections

      Promise.onPossiblyUnhandledRejection(function (error) {
        throw error;
      });

      and discard a rejection, just handle it with an empty catch

      Promise.reject("error value").catch(function () {});

    ⬆ Back to Top

  9. What is deno

    Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 JavaScript engine and the Rust programming language.

    ⬆ Back to Top

  10. How do you make an object iterable in javascript

    By default, plain objects are not iterable. But you can make the object iterable by defining a Symbol.iterator property on it.

    Let's demonstrate this with an example,

    const collection = {
      one: 1,
      two: 2,
      three: 3,
      [Symbol.iterator]() {
        const values = Object.keys(this);
        let i = 0;
        return {
          next: () => {
            return {
              value: this[values[i++]],
              done: i > values.length,
            };
          },
        };
      },
    };
    
    const iterator = collection[Symbol.iterator]();
    
    console.log(iterator.next()); // → {value: 1, done: false}
    console.log(iterator.next()); // → {value: 2, done: false}
    console.log(iterator.next()); // → {value: 3, done: false}
    console.log(iterator.next()); // → {value: undefined, done: true}

    The above process can be simplified using a generator function,

    const collection = {
      one: 1,
      two: 2,
      three: 3,
      [Symbol.iterator]: function* () {
        for (let key in this) {
          yield this[key];
        }
      },
    };
    const iterator = collection[Symbol.iterator]();
    console.log(iterator.next()); // {value: 1, done: false}
    console.log(iterator.next()); // {value: 2, done: false}
    console.log(iterator.next()); // {value: 3, done: false}
    console.log(iterator.next()); // {value: undefined, done: true}

    ⬆ Back to Top

  11. What is a Proper Tail Call

    First, we should know about tail call before talking about "Proper Tail Call". A tail call is a subroutine or function call performed as the final action of a calling function. Whereas Proper tail call(PTC) is a technique where the program or code will not create additional stack frames for a recursion when the function call is a tail call.

    For example, the below classic or head recursion of factorial function relies on stack for each step. Each step need to be processed upto n * factorial(n - 1)

    function factorial(n) {
      if (n === 0) {
        return 1;
      }
      return n * factorial(n - 1);
    }
    console.log(factorial(5)); //120

    But if you use Tail recursion functions, they keep passing all the necessary data it needs down the recursion without relying on the stack.

    function factorial(n, acc = 1) {
      if (n === 0) {
        return acc;
      }
      return factorial(n - 1, n * acc);
    }
    console.log(factorial(5)); //120

    The above pattern returns the same output as the first one. But the accumulator keeps track of total as an argument without using stack memory on recursive calls.

    ⬆ Back to Top

  12. How do you check an object is a promise or not

    If you don't know if a value is a promise or not, wrapping the value as Promise.resolve(value) which returns a promise

    function isPromise(object) {
      if (Promise && Promise.resolve) {
        return Promise.resolve(object) == object;
      } else {
        throw "Promise not supported in your environment";
      }
    }
    
    var i = 1;
    var promise = new Promise(function (resolve, reject) {
      resolve();
    });
    
    console.log(isPromise(i)); // false
    console.log(isPromise(promise)); // true

    Another way is to check for .then() handler type

    function isPromise(value) {
      return Boolean(value && typeof value.then === "function");
    }
    var i = 1;
    var promise = new Promise(function (resolve, reject) {
      resolve();
    });
    
    console.log(isPromise(i)); // false
    console.log(isPromise(promise)); // true

    ⬆ Back to Top

  13. How to detect if a function is called as constructor

    You can use new.target pseudo-property to detect whether a function was called as a constructor(using the new operator) or as a regular function call.

    1. If a constructor or function invoked using the new operator, new.target returns a reference to the constructor or function.
    2. For function calls, new.target is undefined.
    function Myfunc() {
       if (new.target) {
          console.log('called with new');
       } else {
          console.log('not called with new');
       }
    }
    
    new Myfunc(); // called with new
    Myfunc(); // not called with new
    Myfunc.call({}); // not called with new

    ⬆ Back to Top

  14. What are the differences between arguments object and rest parameter

    There are three main differences between arguments object and rest parameters

    1. The arguments object is an array-like but not an array. Whereas the rest parameters are array instances.
    2. The arguments object does not support methods such as sort, map, forEach, or pop. Whereas these methods can be used in rest parameters.
    3. The rest parameters are only the ones that haven’t been given a separate name, while the arguments object contains all arguments passed to the function

    ⬆ Back to Top

  15. What are the differences between spread operator and rest parameter

    Rest parameter collects all remaining elements into an array. Whereas Spread operator allows iterables( arrays / objects / strings ) to be expanded into single arguments/elements. i.e, Rest parameter is opposite to the spread operator.

    ⬆ Back to Top

  16. What are the different kinds of generators

    There are five kinds of generators,

    1. Generator function declaration:

      function* myGenFunc() {
        yield 1;
        yield 2;
        yield 3;
      }
      const genObj = myGenFunc();
    2. Generator function expressions:

      const myGenFunc = function* () {
        yield 1;
        yield 2;
        yield 3;
      };
      const genObj = myGenFunc();
    3. Generator method definitions in object literals:

      const myObj = {
        *myGeneratorMethod() {
          yield 1;
          yield 2;
          yield 3;
        },
      };
      const genObj = myObj.myGeneratorMethod();
    4. Generator method definitions in class:

      class MyClass {
        *myGeneratorMethod() {
          yield 1;
          yield 2;
          yield 3;
        }
      }
      const myObject = new MyClass();
      const genObj = myObject.myGeneratorMethod();
    5. Generator as a computed property:

      const SomeObj = {
        *[Symbol.iterator]() {
          yield 1;
          yield 2;
          yield 3;
        },
      };
      
      console.log(Array.from(SomeObj)); // [ 1, 2, 3 ]

    ⬆ Back to Top

  17. What are the built-in iterables

    Below are the list of built-in iterables in javascript,

    1. Arrays and TypedArrays
    2. Strings: Iterate over each character or Unicode code-points
    3. Maps: iterate over its key-value pairs
    4. Sets: iterates over their elements
    5. arguments: An array-like special variable in functions
    6. DOM collection such as NodeList

    ⬆ Back to Top

  18. What are the differences between for...of and for...in statements

    Both for...in and for...of statements iterate over js data structures. The only difference is over what they iterate:

    1. for..in iterates over all enumerable property keys of an object
    2. for..of iterates over the values of an iterable object.

    Let's explain this difference with an example,

    let arr = ["a", "b", "c"];
    
    arr.newProp = "newVlue";
    
    // key are the property keys
    for (let key in arr) {
      console.log(key); // 0, 1, 2 & newValue
    }
    
    // value are the property values
    for (let value of arr) {
      console.log(value); // a, b, c
    }

    Since for..in loop iterates over the keys of the object, the first loop logs 0, 1, 2 and newProp while iterating over the array object. The for..of loop iterates over the values of a arr data structure and logs a, b, c in the console.

    ⬆ Back to Top

  19. How do you define instance and non-instance properties

    The Instance properties must be defined inside of class methods. For example, name and age properties defined inside constructor as below,

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

    But Static(class) and prototype data properties must be defined outside of the ClassBody declaration. Let's assign the age value for Person class as below,

    Person.staticAge = 30;
    Person.prototype.prototypeAge = 40;

    ⬆ Back to Top

  20. What is the difference between isNaN and Number.isNaN?

    1. isNaN: The global function isNaN converts the argument to a Number and returns true if the resulting value is NaN.
    2. Number.isNaN: This method does not convert the argument. But it returns true when the type is a Number and value is NaN.

    Let's see the difference with an example,

    isNaN(‘hello’);   // true
    Number.isNaN('hello'); // false

    ⬆ Back to Top

  21. How to invoke an IIFE without any extra brackets?

    Immediately Invoked Function Expressions(IIFE) requires a pair of parenthesis to wrap the function which contains set of statements.

    (function (dt) {
      console.log(dt.toLocaleTimeString());
    })(new Date());

    Since both IIFE and void operator discard the result of an expression, you can avoid the extra brackets using void operator for IIFE as below,

    void function (dt) {
      console.log(dt.toLocaleTimeString());
    }(new Date());

    ⬆ Back to Top

  22. Is that possible to use expressions in switch cases?

    You might have seen expressions used in switch condition but it is also possible to use for switch cases by assigning true value for the switch condition. Let's see the weather condition based on temparature as an example,

    const weather = (function getWeather(temp) {
      switch (true) {
        case temp < 0:
          return "freezing";
        case temp < 10:
          return "cold";
        case temp < 24:
          return "cool";
        default:
          return "unknown";
      }
    })(10);

    ⬆ Back to Top

  23. What is the easiest way to ignore promise errors?

    The easiest and safest way to ignore promise errors is void that error. This approach is ESLint friendly too.

    await promise.catch((e) => void e);

    ⬆ Back to Top

  24. How do style the console output using CSS?

    You can add CSS styling to the console output using the CSS format content specifier %c. The console string message can be appended after the specifier and CSS style in another argument. Let's print the red the color text using console.log and CSS specifier as below,

    console.log("%cThis is a red text", "color:red");

    It is also possible to add more styles for the content. For example, the font-size can be modified for the above text

    console.log(
      "%cThis is a red text with bigger font",
      "color:red; font-size:20px"
    );

    ⬆ Back to Top

  25. What is nullish coalescing operator (??)?

    It is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand. This can be contrasted with the logical OR (||) operator, which returns the right-hand side operand if the left operand is any falsy value, not only null or undefined.

    console.log(null ?? true); // true
    console.log(false ?? true); // false
    console.log(undefined ?? true); // true

    ⬆ Back to Top

  26. How do you group and nest console output?

    The console.group() can be used to group related log messages to be able to easily read the logs and use console.groupEnd()to close the group. Along with this, you can also nest groups which allows to output message in hierarchical manner.

    For example, if you’re logging a user’s details:

    console.group("User Details");
    console.log("name: Sudheer Jonna");
    console.log("job: Software Developer");
    
    // Nested Group
    console.group("Address");
    console.log("Street: Commonwealth");
    console.log("City: Los Angeles");
    console.log("State: California");
    
    // Close nested group
    console.groupEnd();
    
    // Close outer group
    console.groupEnd()

    You can also use console.groupCollapsed() instead of console.group() if you want the groups to be collapsed by default.

    ⬆ Back to Top

  27. What is the difference between dense and sparse arrays?

    An array contains items at each index starting from first(0) to last(array.length - 1) is called as Dense array. Whereas if at least one item is missing at any index, the array is called as sparse.

    Let's see the below two kind of arrays,

    const avengers = ["Ironman", "Hulk", "CaptainAmerica"];
    console.log(avengers[0]); // 'Ironman'
    console.log(avengers[1]); // 'Hulk'
    console.log(avengers[2]); // 'CaptainAmerica'
    console.log(avengers.length); // 3
    
    const justiceLeague = ["Superman", "Aquaman", , "Batman"];
    console.log(justiceLeague[0]); // 'Superman'
    console.log(justiceLeague[1]); // 'Aquaman'
    console.log(justiceLeague[2]); // undefined
    console.log(justiceLeague[3]); // 'Batman'
    console.log(justiceLeague.length); // 4

    ⬆ Back to Top

  28. What are the different ways to create sparse arrays?

    There are 4 different ways to create sparse arrays in JavaScript

    1. Array literal: Omit a value when using the array literal
      const justiceLeague = ["Superman", "Aquaman", , "Batman"];
      console.log(justiceLeague); // ['Superman', 'Aquaman', empty ,'Batman']
    2. Array() constructor: Invoking Array(length) or new Array(length)
      const array = Array(3);
      console.log(array); // [empty, empty ,empty]
    3. Delete operator: Using delete array[index] operator on the array
      const justiceLeague = ["Superman", "Aquaman", "Batman"];
      delete justiceLeague[1];
      console.log(justiceLeague); // ['Superman', empty, ,'Batman']
    4. Increase length property: Increasing length property of an array
      const justiceLeague = ['Superman', 'Aquaman', 'Batman'];
      justiceLeague.length = 5;
      console.log(justiceLeague); // ['Superman', 'Aquaman', 'Batman', empty, empty]

    ⬆ Back to Top

  29. What is the difference between setTimeout, setImmediate and process.nextTick?

    1. Set Timeout: setTimeout() is to schedule execution of a one-time callback after delay milliseconds.
    2. Set Immediate: The setImmediate function is used to execute a function right after the current event loop finishes.
    3. Process NextTick: If process.nextTick() is called in a given phase, all the callbacks passed to process.nextTick() will be resolved before the event loop continues. This will block the event loop and create I/O Starvation if process.nextTick() is called recursively.

    ⬆ Back to Top

  30. How do you reverse an array without modifying original array?

    The reverse() method reverses the order of the elements in an array but it mutates the original array. Let's take a simple example to demonistrate this case,

    const originalArray = [1, 2, 3, 4, 5];
    const newArray = originalArray.reverse();
    
    console.log(newArray); // [ 5, 4, 3, 2, 1]
    console.log(originalArray); // [ 5, 4, 3, 2, 1]

    There are few solutions that won't mutate the original array. Let's take a look.

    1. Using slice and reverse methods: In this case, just invoke the slice() method on the array to create a shallow copy followed by reverse() method call on the copy.

      const originalArray = [1, 2, 3, 4, 5];
      const newArray = originalArray.slice().reverse(); //Slice an array gives a new copy
      
      console.log(originalArray); // [1, 2, 3, 4, 5]
      console.log(newArray); // [ 5, 4, 3, 2, 1]
    2. Using spread and reverse methods: In this case, let's use the spread syntax (...) to create a copy of the array followed by reverse() method call on the copy.

      const originalArray = [1, 2, 3, 4, 5];
      const newArray = [...originalArray].reverse();
      
      console.log(originalArray); // [1, 2, 3, 4, 5]
      console.log(newArray); // [ 5, 4, 3, 2, 1]
    3. Using reduce and spread methods: Here execute a reducer function on an array elements and append the accumulated array on right side using spread syntax

      const originalArray = [1, 2, 3, 4, 5];
      const newArray = originalArray.reduce((accumulator, value) => {
        return [value, ...accumulator];
      }, []);
      
      console.log(originalArray); // [1, 2, 3, 4, 5]
      console.log(newArray); // [ 5, 4, 3, 2, 1]
    4. Using reduceRight and spread methods: Here execute a right reducer function(i.e. opposite direction of reduce method) on an array elements and append the accumulated array on left side using spread syntax

      const originalArray = [1, 2, 3, 4, 5];
      const newArray = originalArray.reduceRight((accumulator, value) => {
        return [...accumulator, value];
      }, []);
      
      console.log(originalArray); // [1, 2, 3, 4, 5]
      console.log(newArray); // [ 5, 4, 3, 2, 1]
    5. Using reduceRight and push methods: Here execute a right reducer function(i.e. opposite direction of reduce method) on an array elements and push the iterated value to the accumulator

      const originalArray = [1, 2, 3, 4, 5];
      const newArray = originalArray.reduceRight((accumulator, value) => {
        accumulator.push(value);
        return accumulator;
      }, []);
      
      console.log(originalArray); // [1, 2, 3, 4, 5]
      console.log(newArray); // [ 5, 4, 3, 2, 1]

    ⬆ Back to Top

  31. How do you create custom HTML element?

    The creation of custom HTML elements involves two main steps,

    1. Define your custom HTML element: First you need to define some custom class by extending HTMLElement class. After that define your component properties (styles,text etc) using connectedCallback method. Note: The browser exposes a function called customElements.define inorder to reuse the element.
      class CustomElement extends HTMLElement {
        connectedCallback() {
          this.innerHTML = "This is a custom element";
        }
      }
      customElements.define("custom-element", CustomElement);
    2. Use custome element just like other HTML element: Declare your custom element as a HTML tag.
       <body>
            <custom-element>
       </body>

    ⬆ Back to Top

  32. What is global execution context?

    The global execution context is the default or first execution context that is created by the JavaScript engine before any code is executed(i.e, when the file first loads in the browser). All the global code that is not inside a function or object will be executed inside this global execution context. Since JS engine is single threaded there will be only one global environment and there will be only one global execution context.

    For example, the below code other than code inside any function or object is executed inside the global execution context.

    var x = 10;
    
    function A() {
      console.log("Start function A");
    
      function B() {
        console.log("In function B");
      }
    
      B();
    }
    
    A();
    
    console.log("GlobalContext");

    ⬆ Back to Top

  33. What is function execution context?

    Whenever a function is invoked, the JavaScript engine creates a different type of Execution Context known as a Function Execution Context (FEC) within the Global Execution Context (GEC) to evaluate and execute the code within that function.

    ⬆ Back to Top

  34. What is debouncing?

    Debouncing is a programming pattern that allows delaying execution of some piece of code until a specified time to avoid unnecessary CPU cycles, API calls and improve performance. The debounce function make sure that your code is only triggered once per user input. The common usecases are Search box suggestions, text-field auto-saves, and eliminating double-button clicks.

    Let's say you want to show suggestions for a search query, but only after a visitor has finished typing it. So here you write a debounce function where the user keeps writing the characters with in 500ms then previous timer cleared out using clearTimeout and reschedule API call/DB query for a new time—300 ms in the future.

    function debounce(func, timeout = 500) {
      let timer;
      return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
          func.apply(this, args);
        }, timeout);
      };
    }
    function fetchResults() {
      console.log("Fetching input suggestions");
    }
    const processChange = debounce(() => fetchResults());

    The debounce() function can be used on input, button and window events

    Input:

    <input type="text" onkeyup="processChange()" />

    Button:

    <button onclick="processChange()">Click me</button>

    Windows event:

    window.addEventListener("scroll", processChange);

    ⬆ Back to Top

  35. What is throttling?

    Throttling is a technique used to limit the execution of an event handler function, even when this event triggers continuously due to user actions. The common use cases are browser resizing, window scrolling etc.

    The below example creates a throttle function to reduce the number of events for each pixel change and trigger scroll event for each 100ms except for the first event.

    const throttle = (func, limit) => {
      let inThrottle;
      return (...args) => {
        if (!inThrottle) {
          func.apply(this, args);
          inThrottle = true;
          setTimeout(() => (inThrottle = false), limit);
        }
      };
    };
    window.addEventListener("scroll", () => {
      throttle(handleScrollAnimation, 100);
    });

    ⬆ Back to Top

  36. What is optional chaining?

    According to MDN official docs, the optional chaining operator (?.) permits reading the value of a property located deep within a chain of connected objects without having to expressly validate that each reference in the chain is valid.

    The ?. operator is like the . chaining operator, except that instead of causing an error if a reference is nullish (null or undefined), the expression short-circuits with a return value of undefined. When used with function calls, it returns undefined if the given function does not exist.

     const adventurer = {
       name: 'Alice',
       cat: {
         name: 'Dinah'
       }
     };
    
     const dogName = adventurer.dog?.name;
     console.log(dogName);
     // expected output: undefined
    
     console.log(adventurer.someNonExistentMethod?.());
     // expected output: undefined

    ⬆ Back to Top

  37. What is an environment record?

    According to ECMAScript specification 262 (9.1):

    Environment Record is a specification type used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of ECMAScript code.

    Usually an Environment Record is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement.

    Each time such code is evaluated, a new Environment Record is created to record the identifier bindings that are created by that code.

    ⬆ Back to Top

  38. How to verify if a variable is an array?

    It is possible to check if a variable is an array instance using 3 different ways,

    1. Array.isArray() method:

      The Array.isArray(value) utility function is used to determine whether value is an array or not. This function returns a true boolean value if the variable is an array and a false value if it is not.

      const numbers = [1, 2, 3];
      const user = { name: 'John' };
      Array.isArray(numbers);  // true
      Array.isArray(user); //false
    2. instanceof operator:

      The instanceof operator is used to check the type of an array at run time. It returns true if the type of a variable is an Array other false for other type.

      const numbers = [1, 2, 3];
      const user = { name: 'John' };
      console.log(numbers instanceof Array);  // true
      console.log(user instanceof Array);  // false
    3. Checking constructor type:

      The constructor property of the variable is used to determine whether the variable Array type or not.

      const numbers = [1, 2, 3];
      const user = { name: 'John' };
      console.log(numbers.constructor === Array);  // true
      console.log(user.constructor === Array);  // false

    ⬆ Back to Top

  39. What is pass by value and pass by reference?

    Pass-by-value creates a new space in memory and makes a copy of a value. Primitives such as string, number, boolean etc will actually create a new copy. Hence, updating one value doesn't impact the other value. i.e, The values are independent of each other.

    let a = 5;
    let b = a;
    
    b++;
    console.log(a, b); //5, 6

    In the above code snippet, the value of a is assigned to b and the variable b has been incremented. Since there is a new space created for variable b, any update on this variable doesn't impact the variable a.

    Pass by reference doesn't create a new space in memory but the new variable adopts a memory address of an initial variable. Non-primitives such as objects, arrays and functions gets the reference of the initiable variable. i.e, updating one value will impact the other variable.

    let user1 = {
       name: 'John',
       age: 27
    };
    let user2 = user1;
    user2.age = 30;
    
    console.log(user1.age, user2.age); // 30, 30

    In the above code snippet, updating the age property of one object will impact the other property due to the same reference.

    ⬆ Back to Top

  40. What are the differences between primitives and non-primitives?

    JavaScript language has both primitives and non-primitives but there are few differences between them as below,

    Primitives Non-primitives
    These types are predefined Created by developer
    These are immutable Mutable
    Compare by value Compare by reference
    Stored in Stack Stored in heap
    Contain certain value Can contain NULL too

    ⬆ Back to Top

  41. How do you create your own bind method using either call or apply method?

    The custom bind function needs to be created on Function prototype inorder to use it as other builtin functions. This custom function should return a function similar to original bind method and the implementation of inner function needs to use apply method call.

    The function which is going to bind using custom myOwnBind method act as the attached function(boundTargetFunction) and argument as the object for apply method call.

     Function.prototype.myOwnBind = function(whoIsCallingMe) {
       if (typeof this !== "function") {
         throw new Error(this + "cannot be bound as it's not callable");
       }
       const boundTargetFunction = this;
       return function() {
         boundTargetFunction.apply(whoIsCallingMe, arguments);
       }
     }

    ⬆ Back to Top

  42. What are the differences between pure and impure functions?

    Some of the major differences between pure and impure function are as below,

Pure function Impure function
It has no side effects It causes side effects
It is always return the same result It returns different result on each call
Easy to read and debug Difficult to read and debug because they are affected by extenal code

⬆ Back to Top

  1. What is referential transparency?

An expression in javascript can be replaced by its value without affecting the behaviour of the program is called referential transparency. Pure functions are referentially transparent.

const add = (x,y) => x + y;
const multiplyBy2 = (x) => x * 2;

//Now add (2, 3) can be replaced by 5.

multiplyBy2(add(2, 3)); 

⬆ Back to Top

  1. What are the possible side-effects in javascript?

A side effect is the modification of state through the invocation of a function or expression. These side effects makes our function impure by default. Below are some side effects which makes function impure,

  1. Making an HTTP request. Asynchronous functions such as fetch and promise are impure.
  2. DOM manipulations
  3. Mutating the input data
  4. Printing to a screen or console: For example, console.log() and alert()
  5. Fetching the current time
  6. Math.random() calls: Modifies the internal state of Math object

⬆ Back to Top

  1. What are compose and pipe functions?

    The "compose" and "pipe" are two techniques commonly used in functional programming to simplify complex operations and make code more readable. They are not native in JavaScript and higher order functions. the compose() applies right to left any number of functions to the output of the previous function.

    ⬆ Back to Top

  2. What is module pattern?

    Module pattern is a designed pattern used to wrap a set of variables and functions together in a single scope returned as an object. JavaScript doesn't have access specifiers similar to other languages(Java, Pythong etc) to provide private scope. It uses IIFE (Immediately invoked function expression) to allow for private scopes. i.e, a closure that protect variables and methods.

    The module pattern look like below,

     (function() {
     // Private variables or functions goes here.
    
    
     return {
         // Return public variables or functions here.
     }
    
    
     })();

    Let's see an example of module pattern for an employee with private and public access,

    const createEmployee = (function () {
       // Private
       const name = "John";
       const department = "Sales";
       const getEmployeeName = () => name;
       const getDepartmentName = () => department;
    
    
       // Public
       return {
         name,
         department,
         getName: () => getEmployeeName(),
         getDepartment: () => getDepartmentName(),
       };
     })();
    
    
     console.log(createEmployee.name);
     console.log(createEmployee.department);
     console.log(createEmployee.getName());
     console.log(createEmployee.getDepartment());

    Note: It mimic the concepts of classes with private variables and methods.

    ⬆ Back to Top

  3. What is Function Composition?

    It is an approach where the result of one function is passed on to the next function, which is passed to another until the final function is executed for the final result.

     //example
     const double = x => x * 2
     const square = x => x * x
    
     var output1 = double(2);
     var output2 = square(output1);
     console.log(output2);
    
     var output_final = square(double(2));
     console.log(output_final);

    ⬆ Back to Top

  4. How to use await outside of async function prior to ES2022?

    Prior to ES2022, if you attempted to use an await outside of an async function resulted in a SyntaxError.

    await Promise.resolve(console.log('Hello await')); // SyntaxError: await is only valid in async function

    But you can fix this issue with an alternative IIFE (Immediately Invoked Function Expression) to get access to the feature.

     (async function() {
       await Promise.resolve(console.log('Hello await')); // Hello await
     }());

    In ES2022, you can write top-level await without writing any hacks.

    await Promise.resolve(console.log('Hello await')); //Hello await

⬆ Back to Top