DEV Community

Prince Thomas
Prince Thomas

Posted on

Function context in JavaScript

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();
Enter fullscreen mode Exit fullscreen mode
Output
John Doe
Enter fullscreen mode Exit fullscreen mode

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 global because when you are running on Node it will be global and on browser, it will be window.

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?.

Image description

Let's take a look at this code below.

var name = "John Doe";
function printName() {
  console.log(this.name);
}
setTimeout(printName, 1000);
Enter fullscreen mode Exit fullscreen mode
Output - Node
undefined
Enter fullscreen mode Exit fullscreen mode
Output - Browser
John Doe
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
Output
John Doe
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
Output - Node
John Doe 2
undefined
Enter fullscreen mode Exit fullscreen mode
Output - Browser
John Doe 2
John Doe 1
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
Output:
{}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
Output
24
24
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
Ouput
24
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
Output - Node
undefined
Enter fullscreen mode Exit fullscreen mode
Output - Browser
""
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
Output - Node/Browser:
John Doe
Enter fullscreen mode Exit fullscreen mode

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)