
ES6 interview questions
Are you a qualified ECMAScript programmer who works on client-side scripting on the internet? If yes, then more training in ECMAScript 6 will certainly help you to crack ES6 interview questions and open the door to more opportunities for you. As an ECMAScript 6 developer, you will be able to use a new syntax to write more complex applications that involve various classes and modules. With features such as arrow functions, typed arrays, and math improvements, you can train yourself to build larger applications as well as libraries. IndiaHires will provide you all the information as to how you can crack ES6 interview questions and answers.
ES6 interview questions list
- What is ES6?Define ES6 and mention the new features of ES6?
- Could you explain the difference between ES5 and ES6?
- Explain about Internationalization and localization?
- What is IIFEs (Immediately Invoked Function Expressions)?
- When should I use Arrow functions in ES6?
- What are the differences between ES6 class and ES5 function constructors?
- Why do we need to use ES6 classes?
- What is Hoisting in JavaScript?
- What is the motivation for bringing Symbols to ES6?
- What are the advantages of using spread syntax in ES6, and how is it different from rest syntax?
- Explain the Prototype Design Pattern?
- What is the difference between .call and .apply?
- What are the actual uses of ES6 WeakMap?
- What is the difference between ES6 Map and WeakMap?
- How to “deep-freeze” object in JavaScript?
- What is the preferred syntax for defining enums in JavaScript?
- Explain the difference between Object.freeze() vs const?
- When are you not supposed to use the arrow functions in ES6? Name three or more cases.
- Can you give an example of a curry function, and why does this syntax have an advantage?
- What are the template literals in ES6?
- What do you understand by Generator function?
Below are the new features listed
- Constants (Immutable variables)
- Scoping
- Arrow functions
- Extended parameter handling
- Template literals
- Extended literals
- Modules
- Classes
- Enhanced Regular expressions
- Enhanced object properties.
- Destructuring Assignment
- Symbol Type
- Iterators
- Generator
- Map/Set & WeakMap/WeakSet
- Typed Arrays
- Built-in Methods
- Promises
- Meta programming
- Internationalization and Localization.
ECMAScript 5 (ES5):
The 5th edition of ECMAScript, standardized in 2009. This standard has been implemented fairly completely in all modern browsers
ECMAScript 6 (ES6)/ ECMAScript 2015 (ES2015):
The 6th edition of ECMAScript, standardized in 2015. This standard has been partially implemented in most modern browsers.
Here are some key differences between ES5 and ES6:
Arrow functions & string interpolation:
const greetings = (name) => { return `hello ${name}`; } const greetings = name => `hello ${name}`;
Const
Const works like a constant in other languages in many ways but there are some caveats. Const stands for ‘constant reference’ to a value. So with const, you can actually mutate the properties of an object being referenced by the variable. You just can’t change the reference itself.
const NAMES = []; NAMES.push("Jim"); console.log(NAMES.length === 1); // true NAMES = ["Steve", "John"]; // error
Block-scoped variables
The new ES6 keyword
let
allows developers to scope variables at the block level.Let
doesn’t hoist in the same wayvar
does.Default parameter values
Default parameters allow us to initialize functions with default values. A default is used when an argument is either omitted or undefined — meaning null is a valid value
// Basic syntax function multiply (a, b = 2) { return a * b; } multiply(5); // 10
Class Definition and Inheritance
ES6 introduces language support for classes (
class
keyword), constructors (constructor
keyword), and theextend
keyword for inheritance.for-of operator
The for…of statement creates a loop iterating over iterable objects
Spread Operator
For objects merging
const obj1 = { a: 1, b: 2 } const obj2 = { a: 2, c: 3, d: 4} const obj3 = {...obj1, ...obj2}
Promises
Promises provide a mechanism to handle the results and errors from asynchronous operations. You can accomplish the same thing with callbacks, but promises provide improved readability via method chaining and succinct error handling.
const isGreater = (a, b) => { return new Promise ((resolve, reject) => { if(a > b) { resolve(true) } else { reject(false) } }) } isGreater(1, 2) .then(result => { console.log('greater') }) .catch(result => { console.log('smaller') })
Modules exporting & importing
const myModule = { x: 1, y: () => { console.log('This is ES5') }} export default myModule;
import myModule from './myModule';
These are APIs that are standard JavaScript APIs that help with different tasks such as collation, number formatting, currency formatting, date and time formatting.
Collation
It is used to search within a set of strings and sort a set of strings. It is parametrized by the locale and Unicode-conscious.
Number Formatting
Numbers can be formatted with localized separators and digit groupings. Other things that include style formatting, numbering, percent, and precision.
Currency formatting
Numbers can be formatted mainly with currency symbols, with localized separators and digit groupings.
Date and time formatting
It has been formatted and ordered with localized separators. The format can be short, long and other parameters, such as the locale and time zone.
It’s an Immediately Invoked Function Expression, or an IIFE for short. It will run immediately after it has been created:
(function IIFE(){
console.log( "Hello!" );
})();
// "Hello!"
This pattern is often used when trying to avoid contaminating the global namespace, because all variables used inside the IIFE (like any other normal function) are not visible outside its scope.
Top 21 Essential Java Developer Interview Questions
I am now using the following thumb rule for functions in ES6 and beyond:
- Use
function
in the global scope and for Object.prototype properties. - Use
class
for object constructors. - Use
=>
everywhere else.
Why use arrow functions almost everywhere?
Scope safety
When arrow functions are used consistently, everything is guaranteed to use the same object as the root. If even a single standard function callback is mixed in with a bunch of arrow functions, there’s a chance that the scope will get messed up.
Compactness
The Arrow functions are easier to read and write to. (This may seem to be an opinion, so I’m going to give a few more examples).
Clarity
When almost everything is an arrow function, any regular function immediately sticks out to define the scope. The developer can always look up the next-higher function statement to see what the object is.
Let’s first look at example of each:
// ES5 Function Constructor
function Person(name) {
this.name = name;
}
// ES6 Class
class Person {
constructor(name) {
this.name = name;
}
}
For simple constructors, they look pretty similar.
The main difference in the constructor comes when using inheritance. If we want to create a Student
class that subclasses Person
and add a studentId
field, this is what we have to do in addition to the above.
// ES5 Function Constructor
function Student(name, studentId) {
// Call constructor of superclass to initialize superclass-derived members.
Person.call(this, name);
// Initialize subclass's own members.
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;
}
}
Some of the reasons you might choose to use Classes:
- Syntax is simpler and less error prone.
- It’s much easier (and again, less error-prone) to set up inheritance hierarchies using the new syntax than the old one.
Class
defends you from the common error of not using anew
constructor function (by having the constructor throw an exception ifthis
is not a valid constructor object).- Calling the parent prototype version of the method is much simpler with a new syntax than the old one (
super.method()
instead ofParentConstructor.prototype.method.call(this)
orObject.getPrototypeOf(Object.getPrototypeOf(this))
.
Consider:
/ **ES5**
var Person = function(first, last) {
if (!(this instanceof Person)) {
throw new Error("Person is a constructor function, use new with it");
}
this.first = first;
this.last = last;
};
Person.prototype.personMethod = function() {
return "Result from personMethod: this.first = " + this.first + ", this.last = " + this.last;
};
var Employee = function(first, last, position) {
if (!(this instanceof Employee)) {
throw new Error("Employee is a constructor function, use new with it");
}
Person.call(this, first, last);
this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.personMethod = function() {
var result = Person.prototype.personMethod.call(this);
return result + ", this.position = " + this.position;
};
Employee.prototype.employeeMethod = function() {
// ...
};
In ES6 classes
// ***ES2015+**
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
personMethod() {
// ...
}
}
class Employee extends Person {
constructor(first, last, position) {
super(first, last);
this.position = position;
}
employeeMethod() {
// ...
}
}
Hoisting is the action of the JavaScript interpreter to move all the variables and function declarations to the top of the current scope. There are two types of hoists:
- variable hoisting – rare
- function hoisting – more common
Where a var (or function declaration) appears within the scope of the declaration, it shall be taken to belong to the whole scope and shall be accessible everywhere.
var a = 2;
foo(); // works because `foo()`
// declaration is "hoisted"
function foo() {
a = 3;
console.log( a ); // 3
var a; // declaration is "hoisted"
// to the top of `foo()`
}
console.log( a ); // 2
21 React JS Interview Questions and Answers
Symbols
are a new, special type of object that can be used as a unique property name in objects. Using Symbol
instead of string
allows different modules to create properties that do not conflict with each other. Symbols
can also be made private, so that no one who does not already have direct access to the Symbol
can access their properties.
Symbols
are a new primitive thing. Just like the number
, string
, and boolean
primitives, the symbol
has a function that can be used to create them. Unlike other primitives, the symbols
do not have a literal syntax (e.g. how string have ''
) “-the only way to create them is with the symbol constructor in the following way:
let symbol = Symbol();
In reality, Symbol's
is just a slightly different way of attaching properties to an object-you could easily provide well-known Symbols
as standard methods, just like Object.prototype.hasOwnProperty
, which appears in everything that inherits from Object
.
ES6 spread syntax is very useful when coding in a functional paradigm as you can easily create copies of arrays or objects without using Object.create
, slice
, or a library function. This language feature is often used in Redux and rx.js projects.
function putDookieInAnyArray(arr) {
return [...arr, 'dookie'];
}
const result = putDookieInAnyArray(['I', 'really', "don't", 'like']); // ["I", "really", "don't", "like", "dookie"]
const person = {
name: 'Todd',
age: 29,
};
const copyOfTodd = { ...person };
The rest syntax of ES6 offers a shorthand for including an arbitrary number of arguments to be passed to a function. It’s like the inverse of the spread syntax, taking data and stuffing it into an array rather than unpacking array of data, and it works in function arguments, as well as in array and object destructuring assignments.
function addFiveToABunchOfNumbers(...numbers) {
return numbers.map(x => x + 5);
}
const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); // [9, 10, 11, 12, 13, 14, 15]
const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4]
const { e, f, ...others } = {
e: 1,
f: 2,
g: 3,
h: 4,
}; // e: 1, f: 2, others: { g: 3, h: 4 }
The Prototype Pattern creates new objects, but instead of creating non-initialized objects, it returns objects that are initialized with values copied from a prototype-or sample-object. The pattern of the prototype is also referred to as the pattern of the properties.
An example of where the Prototype pattern is useful is the initialization of business objects with values that match the default values of the database. The prototype object holds the default values that are copied to the newly created business object.
Classical languages rarely use the Prototype pattern, but the JavaScript being a prototype language uses this pattern in the construction of new objects and their prototypes.
Both .call
and .apply
are used to invoke functions, and the first parameter will be used as the value of this
within the function. However, .call
takes the comma-separated arguments as the next arguments, while .apply
uses the array of arguments as the next argument. An easy way to remember this is C for a call
and a comma-separated and A for apply
and an array of arguments.
function add(a, b) {
return a + b;
}
console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3
WeakMaps provide a way to extend objects from the outside without interference with garbage collection. Whenever you want to extend an object, but can’t apply a WeakMap because it is sealed-or from an external source.
WeakMaps provide a way to extend objects from the outside without interference with garbage collection. Whenever you want to extend an object, but can’t apply a WeakMap because it is sealed-or from an external source.
var map = new WeakMap();
var pavloHero = {
first: "Pavlo",
last: "Hero"
};
var gabrielFranco = {
first: "Gabriel",
last: "Franco"
};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero)); //This is Hero
The interesting aspect of the WeakMaps is that it has a weak reference to the key inside the map. A weak reference means that if the object is destroyed, the garbage collector will remove the entire entry from the WeakMap, freeing up memory.
They both behave differently when the object referred to by their keys / values is deleted. Take the following example code:
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);
})()
The above IIFE is executed there is no way we can refer to {x:12}
and {y:12}
. The Garbage Collector goes ahead and removes the “WeakMap” key b pointer and also removes {y:12}
from memory. But in the case of “Map,” the garbage collector does not remove a pointer from “Map” and does not remove {x:12}
from memory.
WeakMap allows the garbage collector to do his job, but not Map. With manually written maps, the key array would keep references to key objects, preventing them from being garbage collected. In native WeakMaps, references to key objects are kept “weakly” which means that they do not prevent the collection of garbage in the event that there is no other reference to the object.
If you want to make sure that the object is deep frozen, you need to create a recursive function to freeze each property that is of the object type:
Without deep freeze:
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
Object.freeze(person); // make object immutable
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }
With 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
Since 1.8.5 it’s possible to seal and freeze the object, so define the above as:
const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...})
Or
const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}
Object.freeze(DaysEnum);
and voila! JS enums.
However, this does not prevent you from assigning an undesired value to a variable, which is often the main goal of the enums:
let day = DaysEnum.tuesday
day = 298832342 // goes through without any errors
Since its release, ES6 has introduced several new features and methods to JavaScript. Among these new features are the method and const
of Object.freeze()
. Sometimes people get confused between the method Object.freeze()
and the const
but Object.freeze()
and the const
are completely different.
Const:
The const
keyword generates a read-only reference to a value. The variables generated by the const
keyword are immutable. In other words, you can’t reassign them to a variety of values. Trying to reassign a constant variable will result in a TypeError.
let myName = "indiahires"
console.log(myName)
// Uncaught TypeError
myName = "ih"
The const
keyword ensures that the created variable is read-only. But it doesn’t mean that the actual value to which the reference to the const variable is immutable. Even though the variable person is constant. However, you can change the value of your property. But you can not reassign a different value to a constant person.
const person = {
name: "indiahires"
};
// No TypeError
person.name = "ih";
console.log(person.name);
Object.freeze() method:
If you want the value of the person object to be immutable, you have to freeze it using the Object.freeze()
method.
const person = Object.freeze({name: "indiahires"});
// TypeError
person.name = "ih";
The method Object.freeze()
is shallow, which means that it can freeze the properties of the object, not the objects referenced by the properties.
const person = Object.freeze({
name: 'indiahires',
address: {
city:"Chandigarh"
}
});
/*But the person.address object is not immutable, you can add a new property to the person.address object as follows:*/
// No TypeError
person.address.country = "India";
Final words
const prevents reassignment.
Object.freeze() prevents mutability.
Arrow functions should NOT be used:
- When we want function hoisting – as arrow functions are anonymous.
- When we want to use this/arguments in a function – as arrow functions do not have this/arguments of their own, they depend upon their outer context.
- When we want to use named function – as arrow functions are anonymous.
- When we want to use function as a constructor – as arrow functions do not have their own this.
- When we want to add function as a property in object literal and use object in it – as we can not access this (which should be object itself).
Currying is a pattern where a function with more than one parameter is broken into multiple functions that, when called in series, accumulates all the required parameters one at a time. This technique can be useful to make it easier to read and compose code written in a functional style. It is important to note that if a function is to be cured, it needs to start as a single function, and then break out into a sequence of functions, each of which accepts a single parameter.
There is a lot of mathematical and computer science theory behind currying in functional programming and I encourage you to read more on Wikipedia. Interesting fact: Currying is named after a mathematician Haskell Curry, not the food.
Basic example:
const sum = x => y => x + y;
// returns the number 3
sum (2)(1);
// returns a function y => 2 + y
sum (2);
Note that sum (2)(1); produces the same result as we would have had if we had defined it as const sum = (x , y) = > x + y; which we would call const sum = (x , y) = > x + y; Summary (2 , 1);.
If seeing more than one arrow gives you trouble, just realize that you are simply returning everything that is behind the first arrow. In this case it’s returning another function as its return value because JavaScript has first-class functions. Also this approach works, because the nested returned function has access to the scope of its parent function and so conveniently the y => x + y
gets the x from its parent.
First class functions
in functional programming JavaScript has first class functions because it supports functions as arguments and return values.
const someFunction = () => console.log ('Hello World!');
const firstClass1 = functionAsArgument => functionAsArgument ();
firstClass1(someFunction); // Hello World! is printed out
const firstClass2 = () => someFunction;
firstClass1 (firstClass2()); // Hello World! is printed out
Practical example: curry as a partial application
This is an example of partially applied function versus curry function, both doing the same thing using JQuery “on” function:
const partiallyAppliedOnClick = handler => $ ('button').on ('click', handler);
const curryOn = handler => event => $ ('button').on (event, handler);
const curryOnClick = handler => curryOn (handler) ('click');
Curry functions are neat when used for carrying reusable code containers. You take a function with multiple arguments, and you know that one of those arguments will have a specific value, but the other is undecided. Therefore, by using curry as a fancy partial application, you can create a new function that will allow you to deal with undecided arguments only without repeating your code.
Practical example: curry for functional composition
Functional composition is a concept that is worthy of its own post. The problem we’re trying to solve by currying is that JavaScript is full of object-oriented approaches that can be difficult to use with functional code, especially if you want to be a purist (or a hipster) about it.
In functional composition you have a sequence of functions applied all on the same single argument as f(g(x)). This would be your example:
// Object oriented approach
const getUglyFirstCharacterAsLower = str
=> str.substr (0, 1).toLowerCase ();
// Functional approach
const curriedSubstring = start => length => str
=> str.substr(start, length);
const curriedLowerCase = str => str.toLowerCase ();
const getComposedFirstCharacterAsLower = str
=> curriedLowerCase (curriedSubstring (0) (1) (str));
It is also very popular to use a compose function to compose your functions:
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
Using the above-defined functions curriedSubstring
and curriedLowerCase
you can then use compose to do this:
const getNewComposedFirstCharacterAsLower = compose (curriedLowerCase, curriedSubstring (0) (1));
if (getComposedFirstCharacterAsLower ('Martin') === getNewComposedFirstCharacterAsLower ('Martin') {
console.log ('These two provide equal results.');
}
function curry(fn) {
if (fn.length === 0) {
return fn;
}
function _curried(depth, args) {
return function(newArgument) {
if (depth - 1 === 0) {
return fn(...args, newArgument);
}
return _curried(depth - 1, [...args, newArgument]);
};
}
return _curried(fn.length, []);
}
function add(a, b) {
return a + b;
}
var curriedAdd = curry(add);
var addFive = curriedAdd(5);
var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]
Source: JavaScript ES6 curry functions with practical examples
The literal template is a new feature introduced in ES6. It provides an easy way to create multiline strings and perform string interpolation. Template literals allow embedded expressions, also called literal strings.
Previous to ES6, literal templates were referred to as template strings. The literal template is enclosed with the backtick (` x`) character. Placeholders in the literal template are represented by the dollar sign and the curly braces (${expression}). If we need to use the expression in the backticks, we can place the expression in (${expression}).
Basic example:
let str1 = "Hello";
let str2 = "World";
let str = `${str1} ${str2}`;
console.log(str);
A generator gives us a new way to work with iterators and functions. The generator is a special type of function that can be paused one or more times in the middle and can be resumed later. The function declaration * (function keyword followed by an asterisk) is used to define a generator function.
When the generator is called, the code does not run. Instead, it returns a special object that is called the Generator object to manage the execution. Let’s look at the example of generators in ES6.
To learn more about Generators in ES6, follow this link ES6 Generators.
Basic example:
function* gen() {
yield 100;
yield;
yield 200;
}
// Calling the Generator Function
var mygen = gen();
console.log(mygen.next().value);
console.log(mygen.next().value);
console.log(mygen.next().value);
//Output
100
undefined
200
Both for..of
and for..in
statements iterate across lists; the values iterated on are different though, for..in
returns a list of keys to the object being iterated, while for..of
returns a list of values of the numeric properties of the object being iterated.
Here is an example that demonstrates this distinction:
let list = [4, 5, 6];
for (let i in list) {
console.log(i); // "0", "1", "2",
}
for (let i of list) {
console.log(i); // "4", "5", "6"
}
Another distinction is that for..in
operates on any object; it serves as a way to inspect properties on this object. for..of on the other hand, is mainly interested in values of iterable objects. Built-in objects like Map and Set implement Symbol.iterator
property allowing access to stored values.
let pets = new Set(["Cat", "Dog", "Hamster"]);
pets["species"] = "mammals";
for (let pet in pets) {
console.log(pet); // "species"
}
for (let pet of pets) {
console.log(pet); // "Cat", "Dog", "Hamster"
}
Although this has been a pretty long article to follow through, I strongly believe that most of us must have learned what kind of ES6 interview questions asked in the interview.