FrontendDeveloper.in

ECMAScript Interview Questions

  • Question 16

    Weakmap

    WeakMap object is a collection of key/value pairs in which the keys are weakly referenced. For this object, the keys must be objects and the values can be arbitrary values.

    Let's see various methods of weakmap with below example,

    var weakMap = new WeakMap();
    
    var obj1 = {}
    var obj2 = {}
    
    weakMap.set(obj1, 1);
    weakMap.set(obj2, 2);
    weakMap.set({}, {"four": 4});
    
    console.log(weakMap.get(obj2)); // 2
    console.log(weakMap.has({})); // return false even though empty object exists as key. Because the keys have different references
    
    delete obj2;
    console.log(weakMap.get(obj2)); // 2
    weakMap.delete(obj1)
    console.log(weakMap.get(obj1)); //undefined
    
  • Question 17

    Unicode

    Prior to ES6, JavaScript strings are represented by 16-bit character encoding (UTF-16). Each character is represented by 16-bit sequence known as code unit. Since the character set is been expanded by Unicode, you will get unexpected results from UTF-16 encoded strings containing surrogate pairs(i.e, Since it is not sufficient to represent certain characters in just 16-bits, you need two 16-bit code units).

    let str = '𠮷';
    
    console.log(str.length);            // 2
    console.log(text.charAt(0));        // ""
    console.log(text.charAt(1));        // ""
    console.log(text.charCodeAt(0));    // 55362(1st code unit)
    console.log(text.charCodeAt(1));    // 57271(2nd code unit)
    
    console.log(/^.$/.test(str)); // false, because length is 2
    console.log('\u20BB7'); // 7!(wrong value)
    console.log(str === '\uD842\uDFB7'); // true
    

    ECMAScript 6 added full support for UTF-16 within strings and regular expressions. It introduces new Unicode literal form in strings and new RegExp u mode to handle code points, as well as new APIs(codePointAt, fromCodePoint) to process strings.

    let str = '𠮷';
    
    // new string form
    console.log('\u{20BB7}'); // "𠮷"
    
    // new RegExp u mode
    console.log(new RegExp('\u{20BB7}', 'u'));
    console.log(/^.$/u.test(str)); // true
    
    //API methods
    console.log(str.codePointAt(0)); // 134071
    console.log(str.codePointAt(1)); // 57271
    
    console.log(String.fromCodePoint(134071)); // "𠮷"
    
  • Question 18

    Symbols

    Symbol is a new peculiar primitive data type of JavaScript, along with other primitive types such as string, number, boolean, null and undefined. The new symbol is created just by calling the Symbol function. i.e, Every time you call the Symbol function, you’ll get a new and completely unique value. You can also pass a parameter to Symbol(), which is useful for debugging purpose only.

    Even though equality checks on two symbols is always false, it will be true while comparing symbols with .for method due to global registry (i.e, Symbols.for('key') === Symbols.for('key'))

    These symbols are useful to uniquely identify properties or unique constants,

    //1. Object properties
    let id = Symbol("id");
    let user = {
    name: "John",
    age: 40,
    [id]: 111
    };
    
    for (let key in user) {
    console.log(key); // name, age without symbols
    }
    
    console.log(JSON.stringify(user)); // {"name":"John", "age": 40}
    console.log(Object.keys(user)); // ["name", "age"]
    
    console.log( "User Id: " + user[id] ); // Direct access by the symbol works
    
    //2. Unique constants
    const logLevels = {
    DEBUG: Symbol('debug'),
    INFO: Symbol('info'),
    WARN: Symbol('warn'),
    ERROR: Symbol('error'),
    };
    console.log(logLevels.DEBUG, 'debug message');
    console.log(logLevels.INFO, 'info message');
    
    //3. Equality Checks
    
    console.log(Symbol('foo') === Symbol('foo')); // false
    console.log(Symbol.for('foo') === Symbol.for('foo')); // true
    
  • Question 19

    Proxies

    The Proxy object is used to create a proxy for another object, which can intercept and redefine fundamental operations for that object such as property lookup, assignment, enumeration, function invocation etc. These are used in many libraries and some browser frameworks.

    The proxy object is created with two parameters with below syntax,

    let proxy = new Proxy(target, handler)
    
    1. target: Object on which you want to proxy
    2. handler: An object that defines which operations will be intercepted and how to redefine them.

    The property Lookup Behavior of a user proxied object will be as below,

    const target = {
    name: "John",
    age: 3
    };
    
    const handler = {
    get: function(target, prop) {
    return prop in target ?
    target[prop] :
    `${prop} does not exist`;
    }
    };
    
    const user = new Proxy(target, handler);
    console.log(user.name); // John
    console.log(user.age); // 3
    console.log(user.gender); // gender does not exist
    

    These proxies also enforce value validations. Let's take an example with set handler,

    let ageValidator = {
    set: function(obj, prop, value) {
    if (prop === 'age') {
    if (!Number.isInteger(value)) {
    throw new TypeError('The age is not an integer');
    }
    if (value > 200) {
    throw new RangeError('Invalid age');
    }
    }
    
    obj[prop] = value; // The default behavior to store the value
    
    return true; // Indicate success
    }
    };
    
    const person = new Proxy({}, ageValidator);
    
    person.age = 30;
    console.log(person.age); // 30
    person.age = 'old';      // Throws an exception
    person.age = 200;        // Throws an exception
    
  • Question 20

    Promises

    A promise is an object which represent the eventual completion or failure of an asynchronous operation.

    It is in one of these states:

    pending: Represents initial state, neither fulfilled nor rejected. fulfilled: Indicates that the operation is completed successfully. rejected: Indicates that the operation is failed.

    A promise is said to be settled if it is either fulfilled or rejected, but not pending. The instance methods promise.then(), promise.catch(), and promise.finally() are used to associate further action with a promise that becomes settled. And these methods also return a newly generated promise object, which can optionally be used for chaining.

    Screenshot

    The promise chaining structure would be as below,

    const promise = new Promise(function(resolve, reject) {
    setTimeout(() => resolve(1), 1000);
    });
    
    promise.then(function(result) {
    console.log(result); // 1
    
    return result * 2;
    }).then(function(result) {
    console.log(result); // 2
    
    return result * 3;
    }).then(function(result) {
    console.log(result); // 6
    
    return result * 4;
    }).catch(function(error){
    console.log(error);
    });
    
  • Question 21

    Reflect

    Reflection is the ability of a code to inspect and manipulate variables, properties, and methods of objects at runtime. JavaScript already provides Object.keys(), Object.getOwnPropertyDescriptor(), and Array.isArray() methods as classic refection features. In ES6, it has been officially provided through Reflect object. Reflect is a new global object which is used to call methods, construct objects, get and set properties, manipulate and extend properties.

    Unlike most global objects, Reflect is not a constructor. i.e, You cannot use Reflect with the new operator or invoke the Reflect as a function. It is similar to Math and JSON objects in which all the methods of this object are static.

    Let's see the usage of Reflect API with below examples,

    1. Creating objects using Reflect.construct();

    The construct() method behaves like the regular new operator, but as a function. It is equivalent to calling new target(...args) with an option to specify a different prototype. The syntax looks like as below,

    Reflect.construct(target, args [, newTarget]);
    

    The method has below parameters,

    1. target: The target function to call.
    2. argumentsList: An array-like object specifying the arguments with which target should be called.
    3. newTarget: The constructor whose prototype should be used. This is an optional parameter. i.e, If newTarget is not present, its value defaults to target.

    Example:

    class User {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    };
    
    let args = ['John', 'Emma'];
    
    let john = Reflect.construct(
    User,
    args
    );
    
    console.log(john instanceof User);
    console.log(john.fullName); // John Doe
    
    1. Calling a function using Reflect.apply(): Prior to ES6, you can invoke a function with a specified this value and arguments by using the Function.prototype.apply() method.

    For example, you can call max() static method of Math object,

    const max = Function.prototype.apply.call(Math.max, Math, [100, 200, 300]);
    console.log(max);
    

    In ES6, Reflect.apply() provides the same features as Function.prototype.apply() but in a less verbose syntax.

    const max = Reflect.apply(Math.max, Math, [100, 200, 300]);
    console.log(max);
    
    1. Defining a property using Reflect.defineProperty(): The Reflect.defineProperty() method is similar to Object.defineProperty() but it returns a Boolean value indicating whether or not the property was defined successfully instead of throwing an exception.

    The syntax of this method looks like below,

    Reflect.defineProperty(target, propertyName, propertyDescriptor)
    

    Let's define the age property on user object,

    class User {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    };
    
    let john = new User('John', 'Resig');
    
    if (Reflect.defineProperty(john, 'age', {
    writable: true,
    configurable: true,
    enumerable: false,
    value: 33,
    })) {
    console.log(john.age);
    } else {
    console.log('Cannot define the age property on the user object.');
    }
    
    1. Delete property using Reflect.deleteProperty():

    The Reflect.deleteProperty() method is used to delete properties like the delete operator but as a function. It returns Boolean value indicating whether or not the property was successfully deleted.

    const user = {
    name: 'John',
    age: 33
    };
    
    console.log(Reflect.deleteProperty(user, 'age')); // true
    console.log(user.age); // undefined
    
    1. Get property of an object using Reflect.get(): The Reflect.get method is used to get a property on an object like the property accessor syntax but as a function.
    const user = {
    name: 'John',
    age: 33
    };
    
    console.log(Reflect.get(user, 'age')); // 33
    
    1. :
  • Question 22

    Binary and Octal

    ES5 provided numeric literals in octal (prefix 0), decimal (no prefix), and hexadecimal ( 0x) representation. ES6 added support for binary literals and improvements on octal literals.

    1. Binary literals:

    Prior to ES5, JavaScript didn’t provide any literal form of binary numbers. So you need to use a binary string with the help of parseInt()

    const num = parseInt('110',2);
    console.log(num); // 6
    

    Whereas ES6 added support for binary literals using the 0b prefix followed by a sequence of binary numbers (i.e, 0 and 1).

    const num = 0b110;
    console.log(num); // 6
    

    2. Octal literals:

    In ES5, to represent an octal literal, you use the zero prefix (0) followed by a sequence of octal digits (from 0 to 7).

    const num = 055;
    console.log(num); // 45
    
    let invalidNum = 058;
    console.log(invalidNum); // treated as decimal 58
    

    Whereas ES6 represents the octal literal by using the prefix 0o followed by a sequence of octal digits from 0 through 7.

    const num = 055;
    console.log(num); // 45
    
    const invalidNum = 028;
    console.log(invalidNum); // treated as decimal 28
    

    Remember If you use an invalid number in the octal literal, JavaScript will throw a SyntaxError as below,

    const invalidNum = 028;
    console.log(invalidNum); // SyntaxError
    
  • Question 23

    Proper Tail Calls

    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 first one. But the accumulator keeps track of total as an argument without using stack memory on recursive calls.

    The browsers which supports PTC do not generate stack overflow instead shows Infinity with below inputs,

    console.log(factorial(10));
    console.log(factorial(100));
    console.log(factorial(1000));
    console.log(factorial(10000));
    
  • Question 24

    Array find methods

    ES6 introduced few array methods and two of them are Array.find() and Array.findIndex().

    Array.find() This method returns the value of the first element in an array that satisfies the given test. Let's take an example of array with all even elements except one element and use find method to find the odd element.

    let arr = [2, 4, 5, 6, 8, 10];
    
    function isOdd(i) {
    return i % 2 !== 0;
    }
    
    console.log(arr.find(isOdd)); // 5
    

    Array.findIndex()

    This method returns the index of the first element in the array that satisfies the given test. Let's take an example of array with all even elements except one element and use findIndex method to find the index of odd element.

    let arr = [2, 4, 5, 6, 8, 10];
    
    function isOdd(i) {
    return i % 2 !== 0;
    }
    
    console.log(arr.findIndex(isOdd)); //2
    

    ES2016 Or ES7

    ES2015/ES6 introduced a huge set of new features. But ECMAScript 2016 Or ES7 introduced only two new features:

    1. Array.prototype.includes()
    2. Exponentiation operator
  • Question 25

    Array Includes

    Prior to ES7, you have to use indexOf method and compare the result with '-1' to check whether an array element contains particular element or not.

    const array = [1,2,3,4,5,6];
    if(array.indexOf(5) > -1 ){
    console.log("Found an element");
    }
    

    Whereas in ES7, array.prototype.includes() method is introduced as a direct approach to determine whether an array includes a certain value among its entries or not.

    const array = [1,2,3,4,5,6];
    if(array.includes(5)){
    console.log("Found an element");
    }
    

    In addition to this, Array.prototype.includes() handles NaN and Undefined values better than Array.prototype.indexOf() methods. i.e, If the array contains NaN and Undefined values then indexOf() does not return correct index while searching for NaN and Undefined.

    let numbers = [1, 2, 3, 4, NaN, ,];
    console.log(numbers.indexOf(NaN)); // -1
    console.log(numbers.indexOf(undefined)); // -1
    

    On the otherhand, includes method is able to find these elements

    let numbers = [1, 2, 3, 4, NaN, ,];
    console.log(numbers.includes(NaN)); // true
    console.log(numbers.includes(undefined)); // true
    
  • Question 26

    Exponentiation Operator

    The older versions of javascript uses Math.pow function to find the exponentiation of given numbers. ECMAScript 2016 introduced the exponentiation operator, **(similar to other languages such as Python or F#) to calculate the power computation in a clear representation using infix notation.

    //Prior ES7
    const cube = x => Math.pow(x, 3);
    console.log(cube(3)); // 27
    
    //Using ES7
    const cube1 = x => x ** 3;
    console.log(cube1(3)); // 27
    

    ES2017 Or ES8

  • Question 27

    Async functions

    In ES6, Promises were introduced to solve the famous callback hell problem. When a series of nested asynchronous functions need to be executed in order, it leads to a callback hell

    function task() {
    task1((response1) => {
    task2(response1, (response2) => {
    task3(response2, (response3) => {
    // etc...
    };
    });
    });
    }
    

    But the Chained Promises creates complex flow for asynchronous code.

    Async functions were introduced as a combination of promises and generators to give us the possibility of writing asynchronous in a synchronous manner. i.e, This function is going to be declared with the async keyword which enable 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();
    
  • Question 28

    Object values

    Similar to Object.keys which iterate over JavaScript object’s keys, Object.values will do the same thing on values. i.e, The Object.values() method is introduced to returns an array of a given object's own enumerable property values in the same order as for...in loop.

    const countries = {
    IN: 'India',
    SG: 'Singapore',
    }
    Object.values(countries) // ['India', 'Singapore']
    

    By the way, non-object argument will be coerced to an object

    console.log(Object.values(['India', 'Singapore'])); // ['India', 'Singapore']
    console.log(Object.values('India')); // ['I', 'n', 'd', 'i', 'a']
    
  • Question 29

    Object entries

    The Object.entries() method is introduced to returns an array of a given object's own enumerable string-keyed property [key, value] pairs in the same order as for...in loop.

    const countries = {
    IN: 'India',
    SG: 'Singapore',
    }
    Object.entries(countries) // [["IN", "India"], ["SG", "Singapore"]]
    

    By the way, non-object argument will be coerced to an object

    const countriesArr = ['India', 'Singapore'];
    console.log(Object.entries(countriesArr)); // [ ['0', 'India'], ['1', 'Singapore']]
    
    const country = 'India';
    console.log(Object.entries(country)); // [["0", "I"], ["1", "n"], ["2", "d"], ["3", "i"], ["4", "a"]]
    
    console.log(Object.entries(100)); // [], an empty array for any primitive type because it won't have any own properties
    
  • Question 30

    Object property descriptors

    Property descriptors describe the attributes of a property. The Object.getOwnPropertyDescriptors() method returns all own property descriptors of a given object.

    It provides the below attributes,

    1. value: The value associated with the property (data descriptors only).
    2. writable: true if and only if the value associated with the property may be changed
    3. get: A function which serves as a getter for the property.
    4. set: A function which serves as a setter for the property.
    5. configurable: true if and only if the type of this property descriptor may be changed or deleted.
    6. enumerable: true if and only if this property shows up during enumeration of the property.

    The usage of finding property descriptors for any property seems to be as below,

    const profile = {
    age: 42
    };
    
    const descriptors = Object.getOwnPropertyDescriptors(profile);
    console.log(descriptors); //  {age: {configurable: true, enumerable: true, writable: true }}
    
Get LinkedIn Premium at Rs 399