What is context?
What happens when you drop a ball?. If your answer is "the ball will hit the floor", you assumed this happened on Earth (or any place with gravity 😀). What if this happened on Moon?. The answer will be different. Right?. This is because the context is different. So context can be defined as surrounding data that will affect whatever is inside.
Functions and Context
Functions are a block of code that solves a specific problem and functions can be executed anywhere in the program (i.e., you can call it whenever you need it). Functions are never executed in isolation, they will always run in a context. Look at the code below.
var name = "John Doe"
function printName() {
console.log(name);
}
printName();
Output
John Doe
Function printName can access variable name just because the context of the function is the global and the variable is defined on the global.
I called context as
the globalbecause when you are running on Node it will beglobaland on browser, it will bewindow.
The context of the function will determine what variables or methods a function can access. This can be determined statically by just looking at the code. If there is nothing unusual and if the code is predictable there is nothing to worry about right?. Unless there is this.
What is this in JavaScript?
The concept of this is not entirely exclusive to JavaScript. All programming languages have it. Then what's the hustle about this in JavaScript?.
Let's take a look at this code below.
var name = "John Doe";
function printName() {
console.log(this.name);
}
setTimeout(printName, 1000);
Output - Node
undefined
Output - Browser
John Doe
The output may differ in different browsers as well.
The output in the Browser differs from Node because the implementation of setTimeout in both environments is different.
Why?
Functions in JavaScript are very powerful. Unlike other programming languages, functions in JavaScript can act differently. Like in the above code, the output is different because the value of this is bonded at the time of execution, which makes it unpredictable, yet JavaScript does this way.
Let's take a look at another piece of code.
var obj = {
name: "John Doe",
};
obj.printName = function () {
console.log(this.name);
};
obj.printName();
Output
John Doe
In the above code, the context of the function printName is obj. Since the value of this is bonded dynamically, you can access name from the this keyword. In here, a function is attached later on the obj, this is possible only if the this keyword value is bonded dynamically. You can't achieve this on programming languages such as Java or Python.
The Problem
Take a look at the code below.
var name = "John Doe 1";
var obj = {
name: "John Doe 2",
};
function printName() {
console.log(this.name);
}
obj.printName = printName;
obj.printName();
printName();
Output - Node
John Doe 2
undefined
Output - Browser
John Doe 2
John Doe 1
In the above code snippet, we are using the same function in both cases, we are not passing any parameters or anything. But the output is different since the value of this is dependent on the context. So when you see a code like this.
function printName() {
console.log(this.name);
}
You can't determine the output, since it depends on the context. Getting different outputs for the same function is not a good idea, but if you use it correctly, you can do magic with it.
Playing with this
Strict mode
If you are defining a function like this
var name = "John Doe"
function printName() {
console.log(this.name);
}
What will be the context of the function?. It will bond to the global, But if you are running in strict mode, the context will be undefined.
new keyword
In JavaScript, you can invoke a function using the new keyword. In this case, the value of this will be an empty object.
function printName() {
console.log(this);
}
new printName();
Output:
{}
bind, call and apply
Since the context of the function is hard to determine, JavaScript provides some methods on the function to pass context with it.
call/apply
call and apply invokes the function immediately with a given context and arguments. The only difference is how the function arguments passed. In the apply function arguments are passed as an array and in the call function arguments are passed comma separated.
var obj = {
number: 1,
multiply: function (number1, number2) {
console.log(this.number * number1 * number2);
},
};
obj.multiply.call({ number: 2 }, 3, 4); //arguments as comma separated
obj.multiply.apply({ number: 2 }, [3, 4]); // arguments as an array
Output
24
24
Here the context is changed in the first argument of both call and apply, which makes the output 24 instead of 12.
bind
bind is another method available on function, which will return a new function with a given context.
var obj = {
number: 1,
multiply: function (number1, number2) {
console.log(this.number * number1 * number2);
},
};
var multiply = obj.multiply.bind({ number: 2 });
multiply(3, 4);
Ouput
24
Arrow functions
Arrow functions are introduced in ES6 to solve this context issue. this keyword is not bonded in the arrow function.
var obj = {
name: "John Doe",
printName: function () {
setTimeout(function(){
console.log(this.name);
}, 1000);
},
};
obj.printName();
Output - Node
undefined
Output - Browser
""
Here, the context of the function is setTimeout, so the value will depend on that. To solve this:
var obj = {
name: "John Doe",
printName: function () {
setTimeout(() => {
console.log(this.name);
}, 1000);
},
};
obj.printName();
Output - Node/Browser:
John Doe
There will be no context binding on arrow functions, which makes the output of the code more predictable.
Conclusion
Everything in JavaScript works the same as almost every other programming language except the this keyword. The value of the this keyword is determined at the run time. This may make your code unpredictable, but you can achieve almost everything using this.

Top comments (0)