FrontendDeveloper.in

ECMAScript Interview Questions

  • Question 46

    BigInt

    In earlier JavaScript version, there is a limitation of using the Number type. i.e, You cannot safely represent integer values(Number primitive) larger than pow(2, 53). In ES2020,

    BigInt is introduced as the 7th primitive type to represent whole numbers(integers with arbitrary precision) larger than pow(2, 53) - 1(or 9007199254740991 or Number.MAX_SAFE_INTEGER). This is been created by appending n to the end of an integer literal or by calling the function BigInt().

    // 1. Current number system
    const max = Number.MAX_SAFE_INTEGER;
    console.log(max + 1) // 9007199254740992
    console.log(max + 2) // 9007199254740992
    
    // 2. BigInt representation
    const bigInt = 9007199254740991n;
    const bigIntConstructorRep = BigInt(9007199254740991); // 9007199254740991n
    const bigIntStringRep = BigInt("9007199254740991"); // 9007199254740991n
    
    // 3. Typeof usage
    
    console.log(typeof 1)// number
    console.log(typeof 1n)// bigint
    console.log(typeof BigInt('1'))// bigint
    
    // 4. Operators
    
    const previousMaxNum = BigInt(Number.MAX_SAFE_INTEGER);
    console.log(previousMaxNum + 2n); //9007199254740993n (this was not possible before)
    console.log(previousMaxNum -2n); //9007199254740990n
    console.log(previousMaxNum * 2n); //18014398509481982n
    console.log(previousMaxNum % 2n); //1n
    console.log(previousMaxNum / 2n); // 4503599627370495n
    
    // 5. comparison
    console.log(1n === 1); // false
    console.log(1n === BigInt(1)); // true
    console.log(1n == 1); // true
    
  • Question 47

    Dynamic Import

    Static imports supports some of the important use cases such as static analysis, bundling tools, and tree shaking, it is also it's desirable to be able to dynamically load parts of a JavaScript application at runtime.

    The new feature dynamic import is introduced to load a module conditionally or on demand. Since it returns a promise for the module namespace object of the requested module, the module can be resolved or import can now be assigned to a variable using async/await as below

    <script>
    const moduleSpecifier = './message.js';
    import(moduleSpecifier)
    .then((module) => {
    module.default(); // Hello, default export
    module.sayGoodBye(); //Bye, named export
    })
    .catch(err => console.log('loading error'));
    </script>
    
    <script>
    (async function() {
    const moduleSpecifier = './message.js';
    const messageModule = await import(moduleSpecifier);
    messageModule.default(); // Hello, default export
    messageModule.sayGoodBye(); //Bye, named export
    })();
    </script>
    

    and the imported module appears with both default and named exports

    export default () => {
    return "Hello, default export";
    }
    export const sayGoodBye = () => {
    return "Bye, named export"
    }
    

    Note: Dynamic import does not require scripts of type="module"

  • Question 48

    Nullish Coalescing Operator

    The nullish coalescing operator (??) 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 operator replaces || operator to provide default values if you treat empty value or '', 0 and NaN as valid values. This is because the logical OR(||) operator treats(empty value or '', 0 and NaN) as falsy values and returns the right operand value which is wrong in this case. Hence, this operator truly checks for nullish values instead falsy values.

    let vehicle = {
    car: {
    name: "",
    speed: 0
    }
    };
    
    console.log(vehicle.car.name || "Unknown"); // Unknown
    console.log(vehicle.car.speed || 90); // 90
    
    console.log(vehicle.car.name ?? "Unknown"); // ""(empty is valid case for name)
    console.log(vehicle.car.speed ?? 90); // 0(zero is valid case for speed)
    

    In a short note, nullish operator returns a non-nullish value and || operator returns truthy values.

  • Question 49

    String matchAll

    There is String#match method to get all the matches of a string against a regular expression by iterating for each match. However this method gives you the substrings that match.

    The String#matchAll() is a new method added to String prototype, which returns an iterator of all results matching a string against a regular expression.

    const regex = /t(e)(st(\d?))/g;
    const string = 'test1test2';
    const matchesIterator = string.matchAll(regex);
    Array.from(matchesIterator, result => console.log(result));
    

    When you this code in browser console, the matches iterator produces an array for each match including the capturing groups with a few extras.

    ["test1", "e", "st1", "1", index: 0, input: "test1test2", groups: undefined]
    ["test2", "e", "st2", "2", index: 5, input: "test1test2", groups: undefined]
    
  • Question 50

    Optional chaining

    In JavaScript, Long chains of property accesses is quite error-prone if any of them evaluates to null or undefined value. Also, it is not a good idea to check property existence on each item which in turn leads to a deeply-nested structured if statements.

    Optional chaining is a new feature that can make your JavaScript code look cleaner and robust by appending(?.) operator to stop the evaluation and return undefined if the item is undefined or null. By the way, this operator can be used together with nullish coalescing operator to provide default values

    let vehicle = {
    };
    
    let vehicle1 = {
    car: {
    name: 'ABC',
    speed: 90
    }
    };
    
    console.log(vehicle.car.name); // TypeError: Cannot read property 'name' of undefined
    
    console.log(vehicle.car?.name); // Undefined
    console.log(vehicle.car?.speed); // Undefined
    
    console.log(vehicle1.car?.name); // ABC
    console.log(vehicle1.car?.speed); // 90
    
    console.log(vehicle.car?.name ?? "Unknown"); // Unknown
    console.log(vehicle.car?.speed ?? 90); // 90
    
  • Question 51

    Promise.allSettled

    It is really helpful to log(especially to debug errors) about each promise when you are handling multiple promises. The Promise.allSettled() method returns a new promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects describing the outcome of each promise.

    const promise1 = new Promise((resolve, reject) => setTimeout(() => resolve(100), 1000));
    
    const promise2 = new Promise((resolve, reject) => setTimeout(reject, 1000));
    
    Promise.allSettled([promise1, promise2]).then(data => console.log(data)); // [
    // Object { status: "fulfilled", value: 100},
    // Object { status: "rejected", reason: undefined}
    // ]
    

    As per the output, each outcome object returns status field which denotes either "fulfilled"(value present) or "rejected"(reason present)

  • Question 52

    globalThis

    Prior to ES2020, you need to write different syntax in different JavaScript environments(cross-platforms) just to access the global object. It is really a hard time for developers because you need to use window, self, or frames on the browser side, global on the nodejs, self on the web workers side.

    On the other hand, this keyword can be used inside functions for non-strict mode but it gives undefined in strict mode. If you think about Function('return this')() as a solution for above environments, it will fail for CSP enabled environments(where eval() is disabled).

    In the older versions, you can use es6-shim as below,

    var getGlobal = function () {
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
    };
    
    var globals = getGlobal();
    
    if (typeof globals.setTimeout !== 'function') {
    console.log('no setTimeout in this environment or runtime');
    }
    

    In ES2020, globalThis property is introduced to provide a standard way of accessing the global this value across environments.

    if (typeof globalThis.setTimeout !== 'function') {
    console.log('no setTimeout in this environment or runtime');
    }
    
  • Question 53

    import.meta

    The import.meta object was created by the ECMAScript implementation with a null prototype to get context-specific metadata about a JavaScript module. Let's say you are trying to load my-module from a script,

    <script type="module" src="my-module.js"></script>
    

    Now you can access meta information(base URL of the module) about the module using the import.meta object

    console.log(import.meta); // { url: "file:///home/user/my-module.js" }
    

    The above URL can be either URL from which the script was obtained (for external scripts), or the document base URL of the containing document (for inline scripts).

    Note: Remember import is not really an object but import.meta is provided as an object which is extensible, and its properties are writeable, configurable, and enumerable.

  • Question 54

    for..in order

    Prior to ES2020, the specifications did not specify in which order for (a in b) should run. Even though most of the javascript engines/browsers loop over the properties of an object in the order in which they were defined, it is not the case with all scenarios. This has been officially standardized in ES2020.

    var object = {
    'a': 2,
    'b': 3,
    'c': 4
    }
    
    for(let key in object) {
    console.log(key); // a b c
    }
    

    ES2021 Or ES12

    ECMAScript 2021 or ES12 has been released in mid of 2021 with few important features which can be used JavaScript.

  • Question 55

    replaceAll

    The new replaceAll() method from String prototype is used to replace all the occurrences of a string from another string value. Earlier it was not possible to replace all the instances of a substring without the use of regex.

    Before ES2021

    console.log('10101010'.replace(new RegExp('0', 'g'), '1')); // 11111111
    console.log('01010101'.replace(/0/g, '1')); // 11111111
    

    After ES2021

    console.log('10101010'.replaceAll('0', '1')); // 11111111
    console.log('01010101'.replaceAll('0', '1')); // 11111111
    
  • Question 56

    promise.any

    The new promise.any method takes multiple promises and resolves to the value of the first promise which is successfully fulfilled.

    let promise1 = new Promise((resolve) => setTimeout(resolve, 100, 'Resolves after 100ms'));
    let promise2 = new Promise((resolve) => setTimeout(resolve, 200, 'Resolves after 200ms'));
    let promise3 = new Promise((resolve, reject) => setTimeout(reject, 0) );
    
    let promises = [promise1, promise2, promise3];
    
     Promise.any(promises)
    .then( value => console.log(value)); // Resolves after 100ms
    

    In case none of the promises resolved then it throws AggregateError exception.

    (async () => {
    try {
    const output = await Promise.any([
    Promise.reject('Error 1'),
    Promise.reject('Error 2'),
    Promise.reject('Error 3'),
    ]);
    console.log(`Output: ${output}`);
    } catch (err) {
    console.log(`Error: ${err.errors}`);
    }
    })();
    // Error: Error1,Error2,Error3
    
  • Question 57

    WeakRef

    WeakRef provides two new pieces of functionality

    1. creating weak references to objects with the WeakRef class
    2. running user-defined finalizers after objects are garbage-collected, with the FinalizationRegistry class

    WeakRef: weak reference is a reference to an object that doesn’t prevent garbage collection if it is the only reference to the object in the memory.It’s useful when we don’t want to keep the object in memory forever(e.g, WebSocket). The main use of weak references is to implement caches or mappings to large objects for which you don't need to keep it in memory for rarely used objects.

    Prior to ES12, WeakMaps and WeakSets are the only way to kind-of-weakly reference an object in JavaScript. Whereas WeakRef in ES12 provides actual weak references, enabling a window into the lifetime of an object.

    Let's see an example of a weak reference object using WeakRef constructor and read the reference using deref() method

    const myObject = new WeakRef({
    name: ‘Sudheer’,
    age: 34
    });
    console.log(myObject.deref()); //output: {name: “Sudheer”, age: 35}
    console.log(myObject.deref().name); //output: Sudheer
    

    Finalizers: A FinalizationRegistry object lets you request a callback when an object is garbage-collected. It works as a cleanup callback.

    // Create new FinalizationRegistry:
    const reg = new FinalizationRegistry((val) => {
    console.log(val);
    });
    
    (() => {
    // Create new object:
    const obj = {}
    
    // Register finalizer for the "obj" as first argument and value for callback function as second argument:
    reg.register(obj, 'obj has been garbage-collected.')
    })();
    

    Note: The finalization callback does not run immediately after garbage-collecting the event listener, so don't use it for important logic or metrics.

  • Question 58

    Numeric Separators

    Numeric separators are helpful to read large numbers(or numeric literals) in JavaScript by providing separation between digits using underscores(_). In other words, numeric literals are more readable by creating a visual separation between groups of digits.

    For example, one billion and one trillion becomes more readable with _ numeric separator,

    const billion = 1000_000_000;
    console.log(billion); // 1000000000
    
    const trillion = 1000_000_000_000n; // BigInt number
    console.log(trillion); // 1000000000000
    

    It can be used for binary and hex literals as well.

    const binaryLiteral = 0b1010_1010;
    console.log(binaryLiteral);
    const hexLiteral = 0xFF_FF_FF_FF;
    console.log(hexLiteral);
    

    Note: The separator can be placed anywhere within the number for readability purpose.

  • Question 59

    Logical Operators

    Logical assignment operators combines the logical operations(&&, || or ??) with assignment. They are quite useful for assigning default values to variables.

    &&=:

    The &&= operator performs the assignment only when the left operand is truthy.

    let x = 10;
    let y = 20;
    x &&= y;
    console.log(x); // 20
    

    The above logical assignment operation can be expanded to:

    x = x && (x = y);
    (OR)
    if (x) {
    x = y;
    }
    

    ||=:

    The ||= operator performs the assignment only when the left operand is falsy.

    let x = 0;
    let y = 20;
    x ||= y;
    console.log(x); // 20
    

    The above logical assignment operation can be expanded to:

    x = x || (x = y);
    (OR)
    if (!x) {
    x = y;
    }
    

    ??=:

    The ??= operator performs the assignment only when the left operand is null or undefined.

    let x;
    let y = 1;
    x ??= y;
    console.log(x); // 1
    

    The above logical assignment operation can be expanded to:

    x ?? (x = y);
    (OR)
    if (!x) {
    x = y;
    }
    

    ES2022 Or ES13

    ECMAScript 2022 or ES13 has been released in the month of June 2022 with some of important features in JavaScript.

  • Question 60

    Top-level await

    In ES2022, it is possible to use await outside of the asynchronous (async) function scope, which makes it easier to use at the module level. This feature delays the execution of current and parent modules until the imported module is loaded.

    Let's take an example of await usage prior to ES2022,

    import posts from './posts';
    
    const getPosts = async() => {
    let posts = await posts();
    return posts;
    }
    

    The usage of await is straightforward with ES2022 as below,

    let posts = await posts();
    

    There are few use cases to know the benefits of this top-level await.

    Usecases

    1. Dynamic dependency pathing: When you have a dynamic path for a dependency that depends on a runtime value, then await is helpful to load or import the messages at runtime.
    const messages = await import(`./messages-${language}.js`);
    
    1. Dependency fallbacks: If the imported module is failed to load, then fallback module loaded used to load the dependency.
    let lodash;
     try {
    lodash = await import('https://first.domain.com/lodash');
     } catch {
    lodash = await import('https://second.domain.com/lodash');
     }
    
    1. Resource initialization: This feature can be used to initialize an app with Database.
    import { dbConnector} from './dbUtils.js'
    //connect to database
    const connection = await dbConnector.connect();
    export default function(){
    connection.list()
    }
    
Get LinkedIn Premium at Rs 399