Decorators and forwarding, call/apply

In JavaScript, you can work with functions with excellent flexibility. You can pass them around, use them as objects. In this chapter, we are going to show you how to decorate them and forward calls between them.

Using “func.call” for the Context

You can use a unique built-in function method func.call(context, ...args), allowing to invoke a function explicitly setting this.

The syntax is as follows:

func.call(context, arg1, arg2, ...)

The syntax given above runs the func providing the first argument like this, and the following as the arguments.

Let’s check out the following calls :

func(1, 2, 3);
func.call(obj, 1, 2, 3)

Both of them call the func with 1, 2 and 3 arguments. The main difference is that func.call sets this to obj.

Here is an example:

function welcomeSite() {
  console.log(this.name);
}
let site = {
  name: "W3Docs"
};
let book = {
  name: "Javascript"
}; 
// use call to pass different objects as "this"
welcomeSite.call(site); // this = W3Docs
welcomeSite.call(book); // this = Javascript

Here call is used to call welcome with a particular context an phrase, like this:

function welcome(message) {
  console.log(message + ' to ' + this.siteName);
}
let site = {
  siteName: "W3Docs"
};
// site becomes this, and "Welcome" becomes the first argument
welcome.call(site, "Welcome"); //Welcome to W3Docs

Going Multi-argument with "func.apply"

It is also possible to use another built-in method func.apply. The syntax is as follows:

func.apply(context, args)

The syntax above runs the func setting this=context and using an array-like object args as the list of arguments.

For example, the following calls are nearly similar:

func(1, 2, 3);
func.apply(context, [1, 2, 3])

Both of them run func giving it the following arguments: 1,2,3. Note that apply may also set this=context.

Let’s observe the following case:

function welcome(message) {
  console.log(` ${message}  to ${this.siteName}`);
}
let site = {
  siteName: "W3Docs"
};
let messageData = ['Welcome']; // become message
// site becomes this, messageData is passed as a argument (message)
welcome.apply(site, messageData); //Welcome to W3Docs (this=site)

Now, let’s see what the difference between call and apply is: the call expects a list of arguments while apply takes an array-like object with them.

The spread operator ... can pass an array as a list of arguments. In case of using it with call, it is possible to achieve nearly the same as apply.

The following two calls are almost the same:

let args = [1, 2, 3]; 
func.call(context, ...args); // pass an array as list with spread operator
func.apply(context, args); // is same as using apply

The differences between them are minor. Here they are:

  1. The spread operator allows passing iterable args like the list to call.
  2. The apply may accept merely array-like args.

Among the essential uses of the apply, we can mention its ability to pass the call to another function, as follows:

let wrapper = function () {
  return anotherFunction.apply(this, arguments);
};

It is known as call forwarding. The wrapper passes all that it receives: the context this, as well as arguments to anotherFunction returning its result.

Borrowing a method [#method-borrowing]

Now it’s necessary to make another improvement in the hashing function. It looks like this:

function hash(args) {
  return args[0] + ',' + args[1];
}

In this case, it can operate only with two arguments. The arr.join method is used to make it work with any number of args, as follows:

function hash(args) {
  return args.join();
}

But don’t get surprised if it doesn’t work. The reason is that the hash(arguments) is called, and the arguments object is iterable and array-like but not a real array.

In the event of calling join on it, will fail:

function hash() {
  console.log(arguments.join()); // Error: arguments.join is not a function
}
hash(1, 2);

You can apply to a simple and efficient way of using array join. Here it is:

function hash() {
  console.log([].join.call(arguments)); // 1,2
}
hash(1, 2);

It is known as method borrowing.

So, you borrow a join method from a regular array [].join using [].join.call to run it in the context of the arguments. It works as the arr.join(glue) native method algorithm is quite simple.




Do you find this helpful?

Related articles