Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

JavaScript Function Methods

Chapter 35 39 mins

Learning outcomes:

  1. The call() method
  2. The apply() method
  3. Bound functions and the bind() method

Introduction

JavaScript is an object-oriented programming language i.e. it is based and built upon the idea of objects. Everything in JavaScript, excluding the primitive values, is an object.

This simply means that functions are objects as well. And hence, they could have properties and methods defined on them. In fact, functions in JavaScript have a couple of predefined properties and methods defined on them.

We saw two function properties back in the JavaScript Function Basics and JavaScript Function Arguments chapter, namely name and length.

Now is the time to explore the methods of function objects in JavaScript. In particular, we'll see call(), apply() and bind() all of which have extreme practical-level importance in real-world programs.

Let's begin.

The call() method

First in the list, we have the call() method.

The call() method calls a function with a provided value for this as used inside the function and with given arguments for the function.

Simple as that!

Here's the syntax of call():

functionObj.call([thisValue[, arg1[, arg2[, ...[, argN]]]]])

thisValue specifies the value to be used as this inside functionObj. If omitted, it defaults to the global object, or to undefined if the script in running in strict mode. arg1, arg2, all the way to argN, denote arguments to be passed onto the function functionObj.

Let's say we have a function getX() that logs the value of the property x on the this object, as shown below:

function getX() {
   console.log(this.x);
}

When we call getX() in the global scope, as in line 5 above, this inside it resolves down to window and thus we get the value of the global variable x returned.

function getX() {
   console.log(this.x);
}

var x = 10;
getX();
10

However, if we call this function as a method, then this resolves down to the calling object, as shown below:

function getX() {
   console.log(this.x);
}

var x = 10;
var o = {x: 20, getX: getX};

o.getX();

We have already seen all these details related to this back in the JavaScript Functions — Basics chapter.

Now with the call() function method, it's no longer required to create a new method on the object o, as shown above, in order to call getX() with its this value pointing to o. We could simply use the power of call().

Recall that the call() method calls a function with a given this. Let's answer two simple questions to determine our final statement.

Which function are we interested in calling over here? You're right, it's getX().

And which object do we want this inside getX() to point to? You're right again, it's the object o.

Likewise we'll just write the statement below to accomplish this:

function getX() {
   console.log(this.x);
}

var x = 10;
var o = {x: 20}

getX.call(o);

The function getX() is called and that with a provided value for this. We're all good to go!

This is just a basic overview of how the call() method works. In practice, there are much more useful cases where the method proves its level of expertise. The two most common ones are demonstrated below.

Using array methods on array-like objects

We've seen one array-like object in the previous part of this course. Can you recall it? It's the arguments object local to and available within all functions.

It looks exactly like an array with indices starting at o containing elements and a length property, however, it is not a true JavaScript array.

That is, it doesn't have access to array methods such as sort(), indexOf(), slice().

If we want to be able to perform array methods as this object, we first ought to convert into one. One way to do so is to code a for loops, iterate over arguments and create an array using its elements. This is super elementary but boring so we won't show it.

We'll show the cool way to do so.

If we look up the internal algorithm of the slice() array method, we see that it doesn't require its this value to be an array — it only has to be like an array (and you know what this means, right?).

So what we could do is to invoke the slice() array method using call() and configure its this value to arguments. This would perform the slice operation on the arguments object.

But from where to where should the slicing happen?

Well, slice() takes two optional arguments start and end — if they are omitted, they default to 0 and the length of the array, respectively, which simply returns back a shallow copy of the array.

Hence, calling slice() on arguments without giving any arguments to it would merely slice arguments from its very beginning to its very end, and return back the slice in the form of an array.

Cool, isn't this?

Shown below is an example:

function f() {
   var args = [].slice.call(arguments);

   // now args is an array
   console.log(args.indexOf(10));
}

f(50, 10, 30);
f(80, 2, 4, 10);
f(0, 5);
1
3
-1
Note that it's not needed to create a new array here (as we do using the literals). All array methods are defined on the object Array.prototype in JavaScript which means that we could access them directly.

We'll see what's the prototype property when we study about it later in the Object Prototypes chapter. It's a core concept in JavaScript every developer must know.

Moving on, it's not just always mandatory to first convert an array-like object into an array before performing any array operation on it.

Conversion to an array is useful only when multiple array operations are to be performed on the array-like object — in this case, it's sensible and efficient to convert it into an array once and then process the array as desired.

However, if only one array operation is desired to be made on the array-like object, it would be equally cool to do that directly using call() without the intermediary step of converting the object into an array.

For instance, suppose we want to find out whether a function f is called with 20 as an argument, and log 'Invalid!' if it doesn't and 'Valid.' if it does.

In the normal way, we'd do the following:

function f() {
   var args = [].slice.call(arguments);

   if (args.indexOf(20) === -1) {
      console.log('Invalid!');
   }
}

f(20, 50);
f(10, 0);
Valid.
Invalid!

But in the cool way, we'd do this directly:

function f() {
   if ([].indexOf.call(arguments, 20) === -1) {
      console.log('Invalid!');
   }
   else {
      console.log('Valid.')
   }
}

f(20, 50);
f(10, 0);
Valid.
Invalid!

Which one of these do you find cool?

Emulating subclassing

Apart from the aforementioned coolness of call(), it's also an extremely handy utility to emulate subclassing behavior in JavaScript which is not a class-based OOP language.

Note that all of the following information won't make any sense at all unless you are well aware of the idea of constructor functions in JavaScript.

So for now, if you're a complete beginner, just try to appreciate the call() method as used in these code snippets without worrying about understanding each bit of detail. We'll review tihs code in the chapter JavaScript Object Constructors where we'll explain every single line.

On the otherhand, if you're already equipped with all the knowledge about constructors and prototypes in JavaScript, then this section would be superbly easy for you to follow through.

Let's say we are coding an online wireframing application in JavaScript to allow web designers to quickly wireframe layout variations for a given webpage.

In the model for the application's logic, there is a Rectangle class to represent rectangle shapes in the application, that is based over the parent Shape class.

Each Shape instance has two properties: fill that contains an RGB hexadecimal code for the fill color of the shape and stroke that defines the borders around the shape. Extending to this, each Rectangle instance has further two properties: width and height specifying the width and height of the rectangle in pixels.

In JavaScript, we could create these constructors as shown below, without the idea of inheritance:

function Shape(fill, stroke) {
   this.fill = fill ? fill : '#000';
   this.stroke = stroke ? stroke : 'none';
}

function Rectangle(width, height, fill, stroke) {
   this.width = width ? width : 100;
   this.height = height ? height : 100;

   // properties for the parent class
   this.fill = fill ? fill : '#000';
   this.stroke = stroke ? stroke : 'none';
}
The this.fill = fill ? fill : '#000' statement (and other such statements) simply means to use the value for fill if it has been provided or otherwise the default value '#000'.

Note that we could have also used the modern ES6-style default-valued parameters to accomplish this idea, but in that case we could've even avoided this constructor function pattern and used the class and extends keywords instead. The code shown above is meant to be compatible on old browsers, likewise we refrain from using default-valued parameters.

However, this is pretty inefficient and inflexible, since we are rewriting the statements to create the properties fill and stroke inside the Rectangle() function.

We are clearly repeating stuff and likewise going against the DRY (Don't Repeat Yourself) principle in programming.

A much more efficient way is to use the call() method to invoke the constructor function Shape() from within Rectangle() and pass it the this value available inside Rectangle(). This would simply execute Shape() as if it was called on the Rectangle instance being created.

Consider the code below:

function Shape(fill, stroke) {
   this.fill = fill ? fill : '#000';
   this.stroke = stroke ? stroke : 'none';
}

function Rectangle(width, height, fill, stroke) {
   this.width = width ? width : 100;
   this.height = height ? height : 100;

Shape.call(this, fill, stroke); }

Now, as you can see, the statements written inside Shape() are seen nowhere else in the code — they are only within Shape().

All classes that are based on Shape (in this case there is only one) call it via the statement Shape.call(this, fill, stroke). In this way, each Shape's logic remains within the Shape() function.

In a large object-oriented JavaScript application, this model of inheritance based on the call() function method could prove extremely useful and powerful.

apply() and bind() could emulate subclassing too!

As we shall see below, this subclassing emulation could also be achieved using the other two function methods, namely apply() and bind(). However, using call() is the simplest of all without any intermediary step.

For apply(), we have to put the arguments for the calling function object in an array.

For bind(), we have to invoke the returned function manually since bind() doesn't invoke the respective function object on which it is called.

Alright, it's time to move on to the second function method — apply().

The apply() method

Quite similar to call(), the apply() function method is also an extremely handy method in JavaScript.

Back when there was no spread operator in JavaScript, a similar result was achieved using apply().

But how? Let's see it...

The apply() function method calls a function with a provided value for this and with given arguments passed to apply() in the form of an array.

The only difference between call() and apply() lies in the last part of the definition above.

That is, as we've seen above, the call() method takes arguments for the underlying function in the form of arguments. For instance, if a function f takes two arguments a and b, then to call it with call() we'd go like f.call(thisValue, a, b).

The apply() method on the otherhand takes arguments for the underlying function in the form of an array. For instance, going with the same example as before, if a function f takes two arguments a and b, then to call it with apply() we'd go like f.apply(thisValue, [a, b]).

See the difference?

In other words, apply() serves to convert an array into a list of arguments for a given function. We could use it to call any function/method in JavaScript that accepts a list of arguments, but what we have in hand is only an array.

Can you recall any such case from the JavaScript Function Arguments chapter, where we have an array in hand but the function/method to be called requires a list of args?

Well, it was the Math.min() method.

Math.min() and Math.max() returns the minimum and maximum value from the list arguments supplied to them.

Take note of the phrase 'list of arguments'. Both these methods don't iterate over an array value provided to them to search for the minimum and maximum value inside it — they merely take the array as another value, coerce it into a number and determine the minimum of all the provided arguments including this coerced array.

Now, it's often the case that the set of numbers whose minimum or maximum value is to be determined is available as an array. In this case, as stated before, we can't just pass in the array to Math.min() or Math.max() expecting to get the desired result.

Rather what's required is to first convert the array into a list of args and then call the respective method using this list.

In the chapter JavaScript Function Arguments, we did so using the spread operator as follows:

var nums = [1, 50, 9, -4, -50, 0, 0, 10, 15, 13];
var min = Math.min(...nums);

console.log('The minimum number is:', min);
The minimum number is: -50

However, the spread operator is one of the relatively recent features of JavaScript and likewise not very well-supported on older browsers. To get around this, we have to use apply().

In the code below, we rewrite the code above using the apply() function method:

var nums = [1, 50, 9, -4, -50, 0, 0, 10, 15, 13];
var min = Math.min.apply(null, nums);

console.log('The minimum number is:', min);

Everything is the same except for line 2. The apply() method is called on the Math.min method since we have to use Math.min() to figure out the minimum value.

The first arg to apply() specifies the value of this to use inside the underlying function — in this case the Math.min() method. Now since, all methods on the Math object don't use this in any way, we could configure it to the value null, which is exactly what we do.

After this, follows an array containing the arguments for the underlying function. In our case, this array is nums likewise we provide it as the second arg to apply().

Internally, the apply() method would iterate over nums and create a list of arguments out of it, and finally call the Math.min() method using this list of arguments.

The result is that we are able to find the minimum value amongst an array of numbers.

Superbly cool!

Now you might be thinking that which one of these two ways, to convert an array into a list of args, should you use. Well, it totally depends on the use case.

If you're making a large-scale application for the web that has to be supported on old browsers as well, and you're not wanting to use any transpiling tools that could convert modern JavaScript into old JavaScript automatically, then you must use apply().

But if you're coding a personal website that won't be accessible on old browsers, or for let's say a programming competition online where you know that latest JavaScript features are well-supported, then you must use the spread operator. It's more elegant and efficient as compared to apply().

Now, one last thing is left to be discussed in regards to apply() which is done below.

Remembering the distinction between call() and apply()

There's quite a good chance that we might forget which of the two methods call() and apply() takes arguments for the underlying function in the form of arguments and which one takes them in the form of an array.

It would be great if we could come up with some trick to remember this. Let's see some...

So there are two names 'call' and 'apply', right? Now which one of these immediately hints at invoking a function? 'Call' or 'apply'?

Well, 'call' is the more familiar term. It right away tells that we are about to call a given function. And since 'call' is more familiar, the call() method takes arguments in the form of arguments, which is the more familiar behavior.

Another trick could be to memorize that 'apply' and 'array' both begin with an 'a', likewise apply() takes arguments in the form of an array.

You could come up with any trick of your own and use that to remember this distinction.

Bound functions — the bind() method

Functions are interesting, bound functions are even more! Let's see what they are.

A bound function wraps an ordinary JavaScript function object with a given value of this, along with given arguments.

Didn't understand a word? Don't worry; you'll understand this definition clearly once you understand how to create a bound function and that what specific purpose it serves.

Let's start from scratch.

Normally, when a function in JavaScript is invoked, its this value is set to the calling object, i.e. the object that called the function.

For example in the code below, logThis() is called directly in the global scope, which is assumed to be on the window object, and thus has its this pointing to window:

function logThis() {
   console.log(this);
}

logThis(); // window

Similarly in the code below, logThis() is called by the object person, and thus has this pointing to person:

function logThis() {
   console.log(this);
}

var person = { logThis: logThis };

person.logThis(); // person

The idea is simple: this depends on the object on which a function is called, at runtime; NOT on the object on which it is declared.

However, this normal functionality can be changed using the method bind(). It can get a function to take any value for this that we provide to it; NOT the object on which it is called.

Syntactically, it's similar to call() and apply():

functionObj.bind([thisValue[, arg1[, arg2[, ...[, argN]]]]])

thisValue specifies the value to be used as this inside the function functionObj, while arg1, arg2, all the way to argN, represent arguments to functionObj.

On completion, bind() returns back what we call a bound function.

This is the main difference between bind() and the other two function methods, call() and apply(). That is, bind() doesn't invoke the underlying function unlike call() and apply(). Rather, it returns back another function whose purpose we'll see below.

This returned function, which is called a bound function, has four internal properties according to the ECMAScript spec, that makes it be recognized as a bound function:

  1. [[BoundTargetFunction]] - the function on which bind() was called.
  2. [[BoundArguments]] - holds the set of arguments passed to bind(), starting from its second argument.
  3. [[BoundThis]] - the value of this passed to bind().
  4. [[Call]] - the method to execute the bound function.

When a bound function is called, the interpreter executes the underlying internal [[Call]] property, which in turn invokes the function saved in [[BoundTargetFunction]] by providing it with arguments [[BoundArguments]] and [[BoundThis]] as a value for this.

These internal properties are what make a bound function operate as one.

Akin to normal functions, a bound function also has a [[Call]] internal slot, but this behaves slightly differently than the one on normal functions, in that the value for this is NOT set to the calling context, but rather to [[BoundThis]].

Since bound functions have a slightly different internal behavior than normal functions, they are termed as exotic objects, as per the ECMAScript specification.

Let's consider a quick example:

var name = 'Global';

function logName() {
   console.log(this.name);
}

logName(); // 'Global'


var obj = {name: 'Object'};
var logName2 = logName.bind(obj);

logName2(); // 'Object'
Global
Object

In the code above, first we create a global variable name (which is also available as window.name) holding a simple text value and then a function logName() that logs the value of this.name upon invocation.

Calling this function, in line 7, in the global context, resolves its this value to window, which means that this.name resolves down to window.name, and likewise we get 'Global' logged.

After this, we create an object obj with a single property name, followed by creating another variable logName2 assigned the return value of the invocation expression logName.bind(obj).

This invocation expression simply binds the function logName() with obj as its this value and then returns back this new bound function. logName2 is simply a reference to this bound function.

Calling logName2(), in line 13, results in logName() being executed with its this set to obj, which means that this.name becomes obj.name, and likewise we get 'Object' logged.

Alright, let's now see a practical-level application of bind().

Passing a method to setTimeout()

Consider the code below:

var logger = {
   x: 10,
   log: function() { console.log(this.x); }
};

setTimeout(logger.log, 3000);

We have an object logger with a property x and a method log() which simply logs the value of the expression this.x.

Then, in the end, we set a timeout for 3 seconds after which logger.log() is executed.

Note that, all of the following discussion would sound alien to you unless you know about the timer function setTimeout().

It's an extremely easy concept, so just spare yourself a couple of minutes to read about setTimeout() in the chapter JavaScript Timers and then come back and continue from this very point onwards.

Anyways, so coming back to setTimeout(), developers often forget the fact that passing an object's method to setTimeout() will result in that method's this value resolving down to window, rather than the object on which it was defined.

This is because the method, which is merely a reference to some function object in memory, is called in the global context by the setTimeout() function internally, which clearly means that its this value gets resolved down to window.

Hence, in the code above, once the function saved in logger.log gets fired after the timeout, we get undefined logged in the console, the reason to which is apparent — this.x becomes window.x, there is no global variable x, likewise undefined is returned.

undefined

A typical solution to this, at least in this case, is to refer to the object logger directly in the log() method i.e. use logger.x instead of this.x.

Below we accomplish this very idea:

var logger = {
   x: 10,
   log: function() { console.log(logger.x); }
};

setTimeout(logger.log, 3000);

This works, but not without handcrafted code — we have to manually refer to logger inside the definition for the method log(), and if for some reason the name of the object logger changes later on, we'd have to make the change in logger.x as well.

Although this is not difficult to do, it is senseless.

Ideally all methods of a given object shall refer to the object using this — these methods are defined on the object and hence technically already know it!

A convenient and extremely straightforward solution to this is to bind a value of this with the method and create a bound function out of it.

Consider the modified code below:

var logger = {
   x: 10,
   log: function() { console.log(this.x); }
};

setTimeout(logger.log.bind(logger), 3000);
10

Now our code is a lot more compact, and at the same time a little more flexible; all methods of logger now refer to it using this only, and not the object's identifier itself.

Now you might say: 'well, we are still using the word logger twice, so what's the benefit in using this method?'

It's right that we are repeating the name logger over here as well, but there is a slight advantage of this approach over the previous one where we repeated the name logger inside the logger.log() method.

The method log() is now essentially independent of any changes being made to the name logger. For instance, if logger later on becomes abc, the method would still work since it doesn't refer to the object via its name — rather, it refers to it via the this keyword.

When we are passing logger.log to setTimeout(), it's only during this time that we need to repeat the name logger. This repetition is less error-prone as compared to the previous one. How? Well, the name ought to repeated exactly in the place where it is referred to in the code — not anywhere else.

In other words, when binding logger.log with the object logger via the method bind(), we only need to repeat the name logger here i.e. in the expression logger.log.bind(logger).

This is less error-prone because we could immediately see that the name logger is being repeated just after one or two words in the same statement, and thus quickly replace it whenever we need to.