23 difficult questions for JavaScript interviews

Want to get ready for a JavaScript interview and are looking for questions to practice with? If so - consider that your search is over. The author of the material, the translation of which we publish today, says that he has collected more than two dozen questions on JavaScript intended for those who want to turn from a junior into a senior, for those who seek to successfully pass an interview in the field of front-end development and get an interesting offer from the employer.


image

1. Explain JavaScript Equality Validation Features


Difficulty: *


JavaScript has two operators for checking equality of values. The first is the so-called strict equality operator. The second is the nonstrict equality operator, which can be used to convert the types of checked quantities.

  • Strict Equality Operator (=== ) checks the values ​​for equality without performing type conversions.
  • The non-strict equality operator ( ==) checks the values ​​for equality, converting them to a common type.

var a = "42";
var b = 42;

a == b;         // true
a === b;        // false

Here are some guidelines for using various equality checkers in JavaScript:

  • If any of the compared values ​​can be a value trueor false- try to avoid the operator ==. Use operator=== .
  • Use the operator ===in the event that if you are working with the following values: 0, «»or[] (empty array).
  • In all other cases, you can safely use the operator ==. Moreover, this is not only safe, but also helps simplify the code and improve its readability.

Source

2. Give examples of casting to a logical type of values ​​that are not related to this type


Difficulty: ***


The essence of this question is to find out which values, in the case of converting them to a logical type, turn into false, and which - into true.

Here is a list of values ​​that can be called “falsy”. They, when converting to a logical type, turn into a value false:

  • «» (empty line).
  • 0, -0, NaN(Not a number).
  • null, undefined.

"False" is a logical meaning false.

Any value that is not included in this list, when it is converted to a logical type, turns into true(such values ​​are called "true" - truthy). For instance:

  • «hello».
  • 42.
  • [ ], [ 1, «2», 3 ](arrays).
  • { }, { a: 42 }(objects).
  • function foo() { .. } (functions).

“True” is also a logical meaning true.

Source

3. What is IIFE?


Difficulty: ***


IIFE (Immediately Invoked Function Expression) is an immediately invoked functional expression. Such an expression is executed immediately after creation.

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

This pattern is often used to prevent pollution of the global namespace. The fact is that variables declared in IIFE (as in any other ordinary function) are invisible outside this function.

Source

4. When should I use the arrow functions that appeared in ES6?


Difficulty: ***


Here are simple rules for using the various ways to declare functions that I follow when developing code for environments that support ES6 and newer standards:

  • Use the keyword functionin the global scope and for properties Object.prototype.
  • Use the keyword functionfor object constructors.
  • In other cases, use arrow functions.

As you can see, arrow functions are recommended to be used almost everywhere. There are several reasons for this state of affairs:

  • Convenient work with context. Arrow functions use the value of the thissurrounding context without having their own this. If such functions are used sequentially, without using ordinary functions in complex constructions, this ensures safe work with the context.
  • Compactness. The arrow function code is easier to enter and easier to read. Perhaps this advantage of arrow functions over ordinary ones will seem to you controversial and depending on the point of view of each specific developer.
  • Code clarity. If almost all code is represented by arrow functions, any ordinary function is distinguished in such code by creating its own context. Using arrow functions, the programmer creates a more understandable code in which it is easier to work with than in code without arrow functions this.

Source

5. What is the difference between ES6 classes and function constructors?


Difficulty: ***


Let's look at some examples first.

Constructor function:

function Person(name) {
  this.name = name;
}

ES6 class:

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

When it comes to creating simple objects, the constructors and classes used for this purpose look very similar.

The main difference between constructors and classes appears when using inheritance. If we need to create a class Studentthat is a subclass of the class Personand add a field to this new class studentId, then this is how the code in which the constructors are used and the code in which the classes are used will look like.

Constructor function:

function Student(name, studentId) {
  //      ,   .
  Person.call(this, name);

  //    .
  this.studentId = studentId;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

ES6 class:

class Student extends Person {
  constructor(name, studentId) {
    super(name);
    this.studentId = studentId;
  }
}

Source

6. Tell us about the Function.prototype.bind () method.


Difficulty: ***


To quote MDN: “The method bind()creates a new function that, when called, sets the thisprovided value as the execution context . The set of arguments is also passed to the method, which will be set before the arguments passed to the bound function when it is called. ”

I believe that method. bind()especially useful for binding values thisin class methods that need to be passed to other functions. This technique is often used in React components.

Source 

7. What are anonymous functions commonly used for?


Difficulty: ***


Anonymous functions are used to create IIFE constructions, the variables declared in which do not pollute the global scope.

(function() {
  // - .
})();

Anonymous functions are used as callback functions, which are used only in one place of the program. The code will look more self-sufficient and readable if the callback is announced right in the place where it is used. This eliminates the need to look at the code in search of the function body.

setTimeout(function() {
  console.log('Hello world!');
}, 1000);

Anonymous functions are conveniently used in constructions specific to the functional programming style, or when working with libraries like Lodash (this use case is similar to their use as callbacks).

const arr = [1, 2, 3];
const double = arr.map(function(el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

Source

8. What is the difference between the Object.freeze () method and the const keyword?


Difficulty: ***


Keyword constand method Object.freeze()are completely different things.

The keyword constapplies to bindings (to "variables"). It creates an immutable binding, that is, it is constimpossible to bind something new to a variable (constant) declared using a keyword . A constant cannot be assigned a new value.

const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // Uncaught TypeError: Assignment to constant variable.

The method Object.freeze()works with values. Or rather, with object values. It makes the object immutable, which protects against changes in the value of the properties of this object.

let person = {
    name: "Leonardo"
};
Object.freeze(person);
person.name = "Lima"; // Uncaught TypeError: Cannot assign to read only property 'name' of object
console.log(person);

Please note that the error message is displayed in strict mode. In normal mode, the operation of changing the property of a "frozen" object simply does not work.

Source

9. What is a “generator"?


Difficulty: ***


Generators are functions from which you can “exit” and into which you can “enter” as necessary. Their context (variable bindings) is maintained between sessions of "entry" into them. Generators are declared using a keyword function*. Such a function, when it is first called, does not execute the code, returning a special object, a generator, which allows you to control its execution. To get the next value issued by the generator, you need to call its method next(). Due to this, the function code is executed until it encounters a keyword yieldthat returns a value.

The generator function can be called as many times as you like. Each time a new generator will return. But each generator can be bypassed only once.

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let iterationCount = 0;
    for (let i = start; i < end; i += step) {
        iterationCount++;
        yield i;
    }
    return iterationCount;
}

Source

10. When should generators be used?


Difficulty: ***


If in a nutshell to describe the main useful features of generators, it turns out that they are as follows:

  • The code in which the generator is used determines the moment the next value is received. The generator is only responsible for returning values, it is controlled from the outside.
  • There are asynchronous generators. They allow you to work with asynchronous data streams.

The main thing in generators is that you can get the next value returned by the generator only when it is needed in code that uses the generator. Generators do not return everything at once. In some situations, this feature may be very convenient.

Source

11. What is “raising variables”?


Difficulty: ****


The essence of the concept of "raising variables" is that ads "rise" to the top of the current scope. As a result, the variable can be used before its declaration. Only variable declarations are raised, but not their initialization code. Note that the behavior of variables declared using the keyword varis different from the behavior of variables and constants declared using letand const.

Source

12. What will the following code output?


Difficulty: ****


var output = (function(x) {
  delete x;
  return x;
})(0);

console.log(output);

This code will output 0. The operator is deleteused to delete the properties of objects. And x- this is not an object property - it is a local variable. The operator deletedoes not affect local variables.

Source

13. What will the following code output?


Difficulty: ****


var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);

This code will output xyz. A property companyis not a property of an object emp1, but a property of its prototype. The operator deletedoes not delete the prototype properties of objects. An object emp1does not have its own property company. You can verify this by:

console.log(emp1.hasOwnProperty('company')); // false

If we still need to remove this property, you can do this either by directly contacting the object Employee( delete Employee.company), or by contacting the prototype of the object emp1using its property __proto__( delete emp1.__proto__.company).

Source

14. Tell us about the prototype design pattern.


Difficulty: ****


Prototype is a generic design pattern. It is used to create objects. Objects created using it contain values ​​copied from their prototype (from the sample object). This template is also called the Properties template.

An example of the use of the “prototype” pattern is the initialization of certain objects with standard values ​​stored in the database. Such values ​​recorded in the prototype are copied to new objects without accessing the database.

It should be noted that this pattern is rarely used in classical languages. JavaScript uses a prototype inheritance model. This pattern is used in the design of new objects and their prototypes.

Source

15. What is a “temporary dead zone" in ES6?


Difficulty: ****


The ES6 performed lifting of variables and constants declared using the keywords letand const(this is done and the rise of the entity declared by using keywords var, classand function). However, the code has a zone that extends from entering the scope to declaring a variable or constant. When accessing a variable or constant in this zone, an error will be generated. This is the “Temporal Dead Zone” (TDZ).

//console.log(aLet)  //  ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

In this example, the TDZ ends after the declaration aLet, but not after the aLetvalue is assigned .

Source

16. Can you describe the main difference between the forEach () and map () array methods? In what situations would you prefer one of these methods to another?


Difficulty: ****


In order to understand the difference between these methods, let's talk about the features of each of them.

Here's how it works .forEach():

  • It iterates over the elements of the array.
  • It performs the callback function passed to it for each element of the array.
  • It does not return anything.

const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  //  -  num /  index.
});

// doubled = undefined

Here is a brief description of the method .map():

  • It iterates over the elements of the array.
  • It converts each element of the original array into an element of the new array, calling the function passed to it for each element of the original array.

const a = [1, 2, 3];
const doubled = a.map(num => {
  return num * 2;
});

// doubled = [2, 4, 6]

As a result, it turns out that the main difference between .forEach()and .map()is that it .map()returns a new array. If you need to get the result of converting the elements of the original array without changing this array, then you should choose .map(). If you just need to iterate over the elements of the array - then you can use it .forEach().

Source

17. What is the difference between an undeclared variable, a variable containing a null value, and an undefined variable? How to check a variable for the fact that it is undeclared, as well as null and undefined?


Difficulty: ****


An undeclared variable is created when a value is assigned to an identifier that was not previously declared using var, letor const. Undeclared variables are declared in the global scope, outside the current scope. In strict mode, an exception is thrown when trying to assign a value to an undeclared variable ReferenceError. Using undeclared variables is not recommended - just as using global variables is not recommended. They should be avoided by all means. In order to protect yourself from the consequences of using undeclared variables, use the block try/catch.

function foo() {
  x = 1; //     ReferenceError
}

foo();
console.log(x); // 1

A variable containing undefinedis a declared variable that is not assigned a value. Value undefinedforms its own data type. If the function does not return anything, and the result of its call is written to a variable, then it will fall into this variable undefined. In order to organize a check for undefined, you can use the strict equality operator ( ===) or the operator typeofthat returns a string undefined. Please note that when checking for, undefinedyou should not use the non-strict equality operator ( ==), since it considers the undefinedand values ​​to be equal null.

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true.        undefined!

function bar() {}
var baz = bar();
console.log(baz); // undefined

A variable containing a value nullmust be explicitly set to this value. It symbolizes the absence of meaning and differs from undefined-variable in that the value in it was explicitly assigned to it. In order to check the value on null, it suffices to use the strict equality operator. To check for null, as in the case of checking for undefined, one should not use the non-strict equality operator, which considers the values ​​of nulland equal undefined.

var foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true

console.log(foo == undefined); // true        null!

I try to never leave variables in an undeclared state, or in a state where they are declared, but they are not explicitly assigned any value. If I am not going to write a value to a variable immediately after its declaration, I write to it null. If you use a linter, it usually reports cases of using undeclared variables.

Source

18. Tell us about the design module "Open module"


Difficulty: *****


The “Revealing Module” template is a variation of the “Module” template. The purpose of using this pattern is to support encapsulation and to discover some properties and methods returned in the object literal. Here's what the direct implementation of this template will look like:

var Exposer = (function() {
  var privateVariable = 10;

  var privateMethod = function() {
    console.log('Inside a private method!');
    privateVariable++;
  }

  var methodToExpose = function() {
    console.log('This is a method I want to expose!');
  }

  var otherMethodIWantToExpose = function() {
    privateMethod();
  }

  return {
      first: methodToExpose,
      second: otherMethodIWantToExpose
  };
})();

Exposer.first();        // : This is a method I want to expose!
Exposer.second();       // : Inside a private method!
Exposer.methodToExpose; // undefined

The obvious drawback of this template is that you cannot use private methods when using it.

Source

19. What is the difference between Map and WeakMap objects?


Difficulty: *****


These objects behave differently if a variable containing a reference to an object that is the key of one of the key / value pairs is not available. Here is an example:

var map = new Map();
var weakmap = new WeakMap();

(function() {
    var a = {
        x: 12
    };
    var b = {
        y: 12
    };

    map.set(a, 1);
    weakmap.set(b, 2);
})()

After the execution of IIFE is completed, we will no longer have access to the objects aand b. Therefore, the garbage collector removes the key bfrom weakmapand clears the memory. But the content mapremains the same.

As a result, it turns out that objects WeakMapallow the garbage collector to get rid of those records that are not referenced in external variables. Objects mapstore key / value pairs regardless of the presence or absence of external key references. The same can be said about the implementation of the data structure Mapusing ordinary arrays. The WeakMap“weak” key references are used. They do not interfere with the operation of the garbage collector if there are no other references to the object used as a key.

Source

20. How are parameters passed to the JavaScript function: by reference or by value?


Difficulty: *****


Parameters are always passed by value, but references to objects are written to variables representing objects. Therefore, when an object is transferred to a function and the property of this object is changed, this change is saved in the object even when the function exits. As a result, there is a feeling that the parameters in the function are passed by reference. But if you change the value of the variable representing the object, this change will not affect objects that are outside the function.

Here is an example:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Here is what this code will output:

10
changed
unchanged

Source

21. How to organize a “deep freeze” of an object?


Difficulty: *****


In order to provide “deep freezing” of an object using Object.freeze(), you need to create a recursive function that “freezes” the properties of the object, which are also objects.

Here is an example of an ordinary “freezing” of an object:

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); //   
person.profession.name = "doctor";
console.log(person); // { name: 'Leonardo', profession: { name: 'doctor' } }

Here is the “deep freeze”:

function deepFreeze(object) {
    let propNames = Object.getOwnPropertyNames(object);
    for (let name of propNames) {
        let value = object[name];
        object[name] = value && typeof value === "object" ?
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}
let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

The error message is displayed only in strict mode. In normal mode, the value does not change without error messages.

Source

22. Why are JavaScript programmers having trouble using the this keyword?


Difficulty: *****


The most important thing to understand about thisis that functions do not have a fixed value this. This value depends on how the function is called. If we say that a function is called with some specific value this, this means that this value is determined not during the declaration of the function, but during its call. Here are some features this:

  • If the function is called in the usual form (that is, using the view construct someFunc()), it thiswill refer to the global object (in the browser this window). If the code is executed in strict mode, a thisvalue will be written to undefined.
  • If the function is called as a method of an object, then the keyword thiswill be represented by the object to which the method belongs.
  • call apply, this , call apply.
  • , this .
  • , new, this , prototype -.
  • If the function was created using the bind method , then the thisfunction keyword will be rigidly bound to the value passed bindas the first argument. This is the only exception to the rule that functions do not have a hard-coded value this. Functions created using bindare immutable this.

Source

23. Compare the use of the async / await construct and generators to implement the same functionality


Difficulty: *****


  • When iterating a generator using a method, .next()each call to this method returns a single value using the keyword yield. When using the async / await construct, await expressions are executed sequentially.
  • The async / await design simplifies the implementation of a specific generator use case.
  • The values ​​returned by the generator always have the form {value: X, done: Boolean}, and asynchronous functions return the promises resolved with the value X, or fail.
  • An asynchronous function can be converted to a generator using promises. The following is an example of such a conversion.

Here is the asynchronous function:

//  
async function init() {
    const res1 = await doTask1();
    console.log(res1);

    const res2 = await doTask2(res1);
    console.log(res2);

    const res3 = await doTask3(res2);
    console.log(res3);

    return res3;
}

init();

Here is a similar generator.

//    
function runner(genFn) {
    const itr = genFn();

    function run(arg) {
        let result = itr.next(arg);

        if (result.done) {
            return result.value;
        } else {
            return Promise.resolve(result.value).then(run);
        }
    }

    return run;
}

//   runner    
runner(function* () {
    const res1 = await doTask1();
    console.log(res1);

    const res2 = await doTask2(res1);
    console.log(res2);

    const res3 = await doTask3(res2);
    console.log(res3);

    return res3;
});

Source

Dear Readers! What JavaScript questions did you come up with during your interviews?


All Articles