This content originally appeared on DEV Community and was authored by jackma
1. What does the this keyword refer to in JavaScript?
Core Concept: Understanding the dynamic nature of this and its different bindings.
Standard Answer: In JavaScript, the this keyword is a special identifier that is automatically defined in the scope of every function. Its value is determined by how a function is called, which is known as its “execution context.” It doesn’t refer to the function itself, but rather to the object that the function is a method of.
- In the global context (outside of any function),
thisrefers to the global object, which iswindowin a browser orglobalin Node.js. - When a function is called as a method of an object (
object.myMethod()),thisis bound to the object the method is called on. - When a function is called as a standalone function (not attached to an object),
thiswill default to the global object in non-strict mode, or beundefinedin strict mode. - With arrow functions,
thisis lexically bound, meaning it inherits thethisvalue from its surrounding (enclosing) scope at the time of its creation. - When used with
call,apply, orbind,thisis explicitly set to the object passed as the first argument.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- How does “strict mode” (
'use strict') affect thethiskeyword? - Can you explain the difference between dynamic scope (how
thisworks) and lexical scope? - What happens to
thisinside a function that is used as an event handler?
2. What is the output of the following code and why?
const user = {
name: 'John Doe',
greet: function() {
console.log(`Hello, I'm ${this.name}`);
},
farewell: () => {
console.log(`Goodbye from ${this.name}`);
}
};
user.greet();
user.farewell();
Core Concept: Differentiating this in a regular function versus an arrow function within an object.
Standard Answer:
-
user.greet()will output: “Hello, I’m John Doe”. This is becausegreetis a regular function and it’s called as a method of theuserobject. Therefore,thisinsidegreetrefers to theuserobject itself. -
user.farewell()will output: “Goodbye from undefined” (or an empty string, depending on the environment). This is becausefarewellis an arrow function. Arrow functions do not have their ownthisbinding; they inheritthisfrom their parent scope. In this case, the parent scope is the global scope wherethis.nameis not defined.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- How could you modify the
farewellmethod to correctly print “John Doe” while still keeping it as a property on theuserobject? - If this code were running inside another function, what would
thisrefer to for thefarewellfunction? - What if we assigned
greetto a new variable and then called it? For example:const sayHello = user.greet; sayHello();What would be the output?
3. What are call and apply, and what is the main difference between them?
Core Concept: Understanding explicit this binding and how arguments are passed.
Standard Answer: Both call and apply are methods that exist on all JavaScript functions. They allow you to invoke a function and explicitly specify the this context for that function call. This means you can “borrow” a function and have it execute in the context of an object it wasn’t originally defined on.
The main difference lies in how they handle function arguments:
-
.call(thisArg, arg1, arg2, ...)accepts thethiscontext as its first argument, followed by a comma-separated list of arguments for the function it’s invoking. -
.apply(thisArg, [arg1, arg2, ...])accepts thethiscontext as its first argument, and the second argument must be an array (or an array-like object) containing the arguments for the function.
A simple mnemonic is: C for call and comma, A for apply and array.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- Can you give a practical example where
applywould be more convenient thancall? (Hint: think about dynamic arguments). - What happens if you pass
nullorundefinedas the first argument tocallorapply? - Before the spread syntax (
...), how wasapplycommonly used with arrays for functions likeMath.max()?
4. What is bind and how does it differ from call and apply?
Core Concept: Explaining how bind creates a new function with a permanently bound this.
Standard Answer: The .bind() method also allows you to set the this value for a function. However, unlike call and apply which execute the function immediately, .bind() returns a new function with the this keyword permanently bound to the provided value. This new function can be called later.
The key differences are:
- Execution:
callandapplyinvoke the function immediately.bindreturns a new function that you can execute later. - Arguments: You can pass arguments to
bindjust like you do withcall(a comma-separated list). This is known as “partial application” or “currying,” where you pre-fill some of the arguments.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- Can you provide a common use case for
bind, for example, with event listeners or in React class components? - Once a function has been bound, can you change its
thiscontext again usingcallorapply? Why or why not? - How could you implement your own simple version of the
bindfunction?
5. Explain the output of this code snippet:
var name = "Global";
function printName() {
console.log(this.name);
}
const obj1 = {
name: "Object 1",
print: printName
};
const obj2 = {
name: "Object 2",
print: printName
};
const boundPrint = printName.bind(obj1);
boundPrint();
obj2.print.call(obj1);
Core Concept: Testing the understanding of method calls, explicit binding with bind and call, and binding precedence.
Standard Answer:
-
boundPrint()will output “Object 1”. This is becausebindcreates a new function wherethisis permanently set toobj1. When we callboundPrint(), it executesprintNamewith that pre-configured context. -
obj2.print.call(obj1)will also output “Object 1”. Although we are accessing the function throughobj2, the.call(obj1)method immediately invokes it and explicitly sets thethiscontext toobj1, overriding the default context ofobj2.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- What would be the output if we just called
obj2.print()? - What if we tried this:
const newBound = boundPrint.bind(obj2); newBound();What would it log and why? - How does the concept of “binding precedence” rank
bindvs.call/apply?
6. How can you use call or apply to borrow methods from other objects?
Core Concept: Demonstrating practical application of explicit binding for code reuse.
Standard Answer: You can borrow methods by using call or apply to invoke a method from one object on another object that doesn’t have that method. A classic example is using array methods on arguments, which is an array-like object but not a true array.
For example, to use the slice method on the arguments object to convert it into a real array:
function list() {
// arguments is not a real array, so it doesn't have .slice()
// We "borrow" slice from Array.prototype
const argsArray = Array.prototype.slice.call(arguments);
return argsArray;
}
console.log(list(1, 2, 3)); // [1, 2, 3]
Here, we are calling the slice method and telling it that its this should be the arguments object, allowing it to work as intended.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- Can you show another example, perhaps borrowing
forEachto iterate over aNodeListfromdocument.querySelectorAll? - What makes an object “array-like”? What properties must it have for this to work?
- Besides
argumentsandNodeList, can you name other array-like objects in JavaScript?
7. What is “binding precedence” in JavaScript?
Core Concept: Ranking the different ways this can be determined.
Standard Answer: Binding precedence is the set of rules JavaScript uses to determine the value of this when multiple rules could apply. The order of precedence, from highest to lowest, is:
-
newbinding: When a function is called with thenewkeyword (as a constructor),thisis a newly created object. This has the highest precedence. - Explicit binding: When a function is called using
call,apply, orbind,thisis explicitly set to the object passed as the first argument. - Implicit binding: When a function is called as a method of an object (
obj.method()),thisis bound to that object. - Default binding: If none of the above rules apply,
thisdefaults to the global object (windowin browsers) in non-strict mode, orundefinedin strict mode.
Note: Arrow functions are an exception; they don’t follow these rules and instead take the this of their lexical parent scope.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- Can you write a piece of code that demonstrates
newbinding taking precedence overbind? - Where do arrow functions fit into this hierarchy?
- Explain why
obj.method.call(anotherObj)is an example of explicit binding overriding implicit binding.
8. What is a hard-binding and how can you achieve it?
Core Concept: Understanding how to create a function that is permanently locked to a specific this context.
Standard Answer: Hard-binding is the process of creating a function that, no matter how it’s called, is always bound to a specific this context. The most common and modern way to achieve this is by using the .bind() method.
When you use func.bind(obj), it creates a new function that is permanently “hard-bound” to obj. Any subsequent attempts to change its this context using call, apply, or even implicit binding will be ignored.
Here is an example:
function sayName() {
console.log(this.name);
}
const user = { name: "Alice" };
const otherUser = { name: "Bob" };
const boundSayName = sayName.bind(user);
boundSayName(); // Logs "Alice"
boundSayName.call(otherUser); // Still logs "Alice"
The hard-binding to user cannot be overridden.
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- Before
.bind()was introduced in ES5, how might a developer have created a hard-bound function? (Hint: using a closure). - Is an arrow function a form of hard-binding? Explain your reasoning.
- Can you use hard-binding to create a function that ignores the
thisfrom anewkeyword instantiation?
9. How does this work with constructor functions?
Core Concept: Explaining the new binding rule.
Standard Answer: When a function is called using the new keyword, it acts as a constructor. JavaScript does four things automatically:
- A brand new, empty object is created.
- This new object is linked to the function’s
prototype. - The
thiskeyword inside the constructor function is bound to this newly created object. - If the function does not explicitly return another object, it implicitly returns
this(the new object).
So, inside a constructor function, this refers to the new instance of the object being created.
function Car(make) {
// `this` here refers to the new object being created
this.make = make;
this.isRunning = false;
}
const myCar = new Car('Honda'); // `this` inside Car was bound to myCar
console.log(myCar.make); // "Honda"
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- What happens if you forget to use the
newkeyword when calling a constructor function? - What happens if you explicitly
returna primitive value (like a string or number) from a constructor? - What happens if you explicitly
returnan object from a constructor?
10. Can you implement your own version of Function.prototype.bind?
Core Concept: Demonstrating a deep understanding of closures, this, and apply by creating a polyfill.
Standard Answer: Yes. A custom bind function needs to do three things:
- Accept a context (
thisArg) and return a new function. - Capture any arguments passed during the binding phase (“partial application”).
- When the new function is called, it should invoke the original function with the bound context and a combination of the bound arguments and any new arguments.
Here’s a simplified implementation:
Function.prototype.myBind = function(context, ...boundArgs) {
const originalFunc = this; // 'this' here is the function myBind is called on
// Return a new function
return function(...newArgs) {
// When the new function is called, invoke the original
// with the saved context and all arguments.
return originalFunc.apply(context, [...boundArgs, ...newArgs]);
};
};
// Example Usage:
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Sarah' };
const greetSarah = greet.myBind(person, 'Hello');
greetSarah('!'); // Outputs: "Hello, Sarah!"
Possible Follow-up Questions:
(Want to test your skills? Try a Mock Interviews)
- My implementation uses
applyand spread syntax. How could you achieve the same result without them? - A complete polyfill for
bindalso needs to handle being used with thenewoperator. How would you add that functionality? - What are the advantages of creating polyfills like this?
This content originally appeared on DEV Community and was authored by jackma