Concepts to revise before appearing for a JavaScript Interview (Part — 1)
When going for Frontend Interviews, it’s extremely important that you have the fundamentals of JavaScript down really well. Interviewers will spend a good amount of time testing your depth of knowledge in the language.
In this article, I’ve tried to list and briefly discuss some of the topics that one should definitely know -
this
The value that this stores is the current execution context of the JavaScript program. Thus, the value of this inside a function is determined by how the function is defined, how it is invoked and the default execution context.
Here’s a summary of how ‘this’ works in JavaScript -
- If the new keyword is used when calling the function, this inside the function is a brand new object.
- If apply, call, or bind are used to call/create a function, this inside the function is the object that is passed in as the argument.
- If a function is called as a method, such as obj.method() — this is the object that the function is a property of.
- If a function is invoked as a free function invocation, meaning it was invoked without any of the conditions present above, this is the global object. In a browser, it is the window object. If in strict mode (‘use strict’), this will be undefined instead of the global object.
- If multiple of the above rules apply, the rule that is higher wins and will set the this value.
- If the function is an ES2015 arrow function, it ignores all the rules above and receives the this value of its surrounding scope at the time it is created.
The methods — bind(), call() and apply() are used to bind a function while passing the desired scope to the inner function. Let’s look at them in detail -
bind()
The bind() function is used when you want to maintain the context in asynchronous functions and callbacks.
- You can pass any context to the bind() method. This is the first argument of the bind() method. Whatever is passed as the context, the ‘this’ keyword inside the function to which the bind() method is applied, starts pointing to the passed context.
- You can also pass other arguments to the bind() method. But note that the first argument must always be the context. The rest of the arguments can be anything that the bind() method takes and the function, to which the bind() method is applied, accepts in parameters.
Let’s now look at an example how bind() is used -
var person = {
firstName: "Rishav",
lastName: "Bharti",
get: function () {
console.log("Outer: " + this.firstName + " " + this.lastName);
var print = function (role, language) {
console.log("Inner: " + this.firstName + " " + this.lastName + ". I'm a " + language + " " + role + ".")
}.bind(this, "Developer", "MERN Stack");
print();
},
};
person.get();
Try guessing the output of the program. Answer to it will be revealed towards the end of this article.
call() and apply() is used when you want to use a function inside an object and call that function in an entirely different object.
call()
The bind() and call() methods differ from each other on the basis of the fact that you explicitly need to call the function on which the bind() method is applied. This is because the bind() method returns the function which needs to be called later. However, you do not need to do so in case of the call() method. The function on which you apply the call() method is invoked implicitly when the control reaches it. Thus, you do not need to explicitly call the function on which you apply the call() method.
Here’s an example showing the usage of call() -
var person = {
firstName: "Rishav",
lastName: "Bharti",
get: function () {
console.log("Outer: " + this.firstName + " " + this.lastName);
var print = function (role, language) {
console.log("Inner: " + this.firstName + " " + this.lastName + ". I'm a " + language + " " + role + ".")
}.call(this, "Developer", "MERN Stack");
},
};
person.get();
What do you think will be the output of the above program? We’ll find out soon.
apply()
The only difference between the call() method and apply() method is that call() method accepts arguments passed individually and separated with a comma. On the other hand, the apply() method is called with an array of arguments.
var person = {
firstName: "Rishav",
lastName: "Bharti",
get: function () {
console.log("Outer: " + this.firstName + " " + this.lastName);
var print = function (role, language) {
console.log("Inner: " + this.firstName + " " + this.lastName + ". I'm a " + language + " " + role + ".")
}.apply(this, ["Developer", "MERN Stack"]);
},
};
person.get();
Are you able to guess the output of the above code snippet?
The output of all the above programs will be —
Outer: Rishav Bharti \n Inner: Rishav Bharti. I’m a MERN Stack Developer.
Closures
A closure is basically an inner function that has access to the outer (enclosing) function’s resources due to the scope chain where a child can access all the resources of its parent. It is basically the combination of a function and the lexical environment in which the function was declared. This closure function has access to all the local variables that were declared inside this lexical scope when the closure function was created.
The power of closures is derived from the fact that the inner function remembers the environment in which it was created. In other words, the inner function has access to the outer function’s variables and parameters.
Closures are significant in the situations when you require the inner function to access the outer function’s variables (or resources) as long as the inner function wants, even when the outer function has finished executing.
Consider a scenario where you mock the functionality of adding an item to the cart. A simplified code can be considered as the one given below:
function modifyItem() {
var items = 0, inStock = 10;
return {
add: function() {
if(inStock > 0) {
console.log(“Added item to cart!”);
items++;
inStock — ;
}
else {
console.log(“Item Out of Stock!”);
}
},
getCount: function() {
return “Items in cart = “ + items;
}
}
}var item = modifyItem();
item.add();
console.log(item.getCount()); // prints 1
item.add();
item.add();
console.log(item.getCount()); // prints 3
Here, the function modifyItem() modifies an item inside the cart. It returns an object consisting of two keys — add and getCount, which contain methods to add an item to the cart and to get the count of the total number of items in the cart.
The value returned by the function modifyItem(), which is an object, is held inside the variable item. On this variable item, the getCount() method is called. Notice that the methods — add() and getCount() are accessible even after the statement modifyItem() is executed meaning that these are accessible even after the function modifyItem() has finished executing.
Thus, you can use the closure function (closure methods — add() and getCount()) even when the outer function (function modifyItem()) in which it was defined has finished its execution and you can still access the resource(s) (items) defined inside the scope of the outer function, in which the closure was created.
Hoisting
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution and are remembered by the compiler. This is how, when you run the code, all the variables are accessible even though it seems like the variables haven’t been declared yet. But remember that all these variables are initialized with the value as undefined. And this is why, if you try to access a variable without initializing it, you get the value as undefined.
Prototypes & Prototypal inheritance
In JavaScript, objects have a special hidden property [[Prototype]] (as named in the specification), that is either null or references another object. That object is called “a prototype”.
The __proto__ property either refers to an object or is null. The prototype property of an object is where we put methods and properties that we want other objects to inherit. A prototype of an object can be assigned to another object, thus, making the latter access the resources of the former. This is what is called prototypal inheritance.
- The Constructor’s prototype property is NOT the prototype of the Constructor itself, it’s the prototype of ALL instances that are created through it.
When a certain method (or property) is called, the search moves on to the object’s prototype. This continues until the method is found: prototype chain. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. By definition, null has no prototype, and acts as the final link in this prototype chain. Nearly all objects in JavaScript are instances of Object, which sits on the top of a prototype chain.
In the next article, we’ll discuss some more important concepts.
P.S. — I’ve written this article as a note to myself about the topics I must revise before appearing for an interview