Demystify Array.prototype.slice.call()

And everything you want to know about JavaScript bind, call, apply method

E.Y.

--

Photo by Joanna Kosinska on Unsplash

This is a revisit to an old topic —I admit. But it can be still confusing, until today. I remember when I first saw the usage of Array.prototype.slice.call() — and that was the time when I didn’t grasp the concepts of prototype chain, magic “this” and method borrowing fully at all, it was like blending 10 vegetables in a fancy hipster smoothie and you have to tell the ingredients from looking at the colour without even have a sip of it.

Before we jump into Array.prototype.slice.call() directly, there are a couple of concepts we need to understand first.

Firstly, the “This” .

This is special in JavaScript. It is a property of an execution context (global, function or eval) that, in non–strict mode, is always a reference to an object and in strict mode can be any value.

  • In the global execution context this refers to the global object whether in strict mode or not.
  • Inside a function, the value of this depends on how the function is called. Since the following code is not in strict mode, and because the value of this is not set by the call, this will default to the global object, which is window in a browser. In strict mode, however, if the value of this is not set when entering an execution context, it remains as undefined .
  • Within a class constructor, this is a regular object. All non-static methods within the class are added to the prototype of this:
  • In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object.
  • When a function is called as a method of an object, its this is set to the object the method is called on.

This is important, as all other concepts we will be discussing soon are derived from manipulating “This”.

Function.prototype.call()

The call() method calls a function with a given this value and arguments provided individually.

Its signature is: func.call([thisArg[, arg1, arg2, ...argN]])

call() provides a new value of this to the function/method. With call(), you can write a method once and then inherit it in another object, without having to rewrite the method for the new object. This is particularly useful with describing inheritance relationship between classes:

In the following example, the constructor for the Product object is defined with two parameters: name and price.

Two other functions, Food and Toy, invoke Product, passing this, name, and price. Product initializes the properties name and price, both specialized functions define the category.

function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
function Toy(name, price) {
Product.call(this, name, price);
this.category = 'toy';
}
const cheese = new Food('feta', 5);
const fun = new Toy('robot', 40);

Of course, the first context argument is optional. In the example below, we invoke the display function without passing the first argument. If the first argument is not passed, the value of this is bound to the global object in non-strict mode and undefined in strict mode.

var sData = 'Wisen';function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // sData value is Wisen========================>
'use strict';
var sData = 'Wisen';function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // Cannot read the property of 'sData' of undefined

Function.prototype.apply()

apply() and call() are very identical except that call() accepts an argument list, while apply() accepts a single array-like arguments.

Its signature is: func.apply(thisArg, [ argsArray])

What is array-like?

Some JavaScript objects, such as the NodeList returned by document.getElementsByTagName() or the arguments object made available within the body of a function, look and behave like arrays on the surface in that they have indexed access and a length property, but do not share all of their methods.

A typical example is arguments :

arguments[0] // first argument
arguments[1] // second argument
arguments[2] // third argument

The arguments object is not an Array. It is similar, but lacks all Array properties except length.

And finally, Function.prototype.bind()

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

let boundFunc = func.bind(thisArg[, arg1[, arg2[, ...argN]]])

As you can see, the bind() function creates a new bound function, which is an exotic function object (a term from ECMAScript 2015) that wraps the original function object. Calling the bound function generally results in the execution of its wrapped function.

A little on exotic function object:

All objects in JavaScript can be divided into two kinds: ordinary and exotic objects. The former has standard, predictable behavior. Exotic objects, on the other hand, might surprise you with its behaviour, good or bad. Some of the builtin object types that are exotic: Array, Proxy, String, Arguments, Module.

For example with Array, it has a length property which automatically increases when indexed properties get added, and can have an impact on other properties when manipulated:

let arr = [];
arr.length; // 0
arr[0] = "a";
arr.length; // 1
arr.length = 0;
arr[0]; // undefined

The simplest use of bind() is to make a function that, no matter how it is called, is called with a particular this value. This is helpful when you want to use a function outside of the context it’s created but still need to access the internal state of its original context:

this.x = 9;    
const module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81const retrieveX = module.getX;
retrieveX(); // 9

To still get x value to original 81, we need to bind the context of retrieveX to original module:

const boundGetX = retrieveX.bind(module);
boundGetX(); // 81

The differences of bind, call and apply

You may notice that call/apply invokes the function immediately, while bind returns a function that, when later executed, will have the context set earlier. It is particularly useful in maintaining context in async callbacks and events.

For apply and call, since one takes an argument list, the other takes an array-like arguments, you should pick apply when you don’t know the exact number of arguments that the functions need so you can unpack the arguments easily:

const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
console.log(max);
// 7

Note that in above example, the use of null with .apply() or .call() is only done for namespace reasons, not for object-oriented reasons, as max() is a method on the Math module only because of namespace , not because the Math object has instance data that the method .max() needs to access, otherwise, call null without a context that provides the instance data will raise error.

Another use case of bind thanks to its “delayed calling”, is Partial.

Partial is a functional programming concept. A partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

These fixed arguments (if any) follow the provided this value and are then inserted at the start of the arguments passed to the target function, followed by whatever arguments are passed bound function at runtime.

To use the example from MDN:

function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2
}
const list1 = list(1, 2, 3);
// [1, 2, 3]
const result1 = addArguments(1, 2);
// 3
// 37 is fixed
const leadingThirtysevenList = list.bind(null, 37);
// 37 is fixed
const addThirtySeven = addArguments.bind(null, 37);
const list2 = leadingThirtysevenList();
// [37]
const list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]
const result2 = addThirtySeven(5);
// 37 + 5 = 42
const result3 = addThirtySeven(5, 10);
// 37 + 5 = 42
// (the second argument is ignored

A final remark

As we can see, regardless of calling it later or earlier, call apply and bind are useful methods to enable re-using of built-in functions in different modules. We don’t need to specifically write a child class to inherit or extend from the parent class just to access one method. This is an important aspect of function borrowing, with one of the most popular native methods being Array.prototype.slice

Now let’s look at the Array.prototype.slice() :

There are array-like objects that behave like arrays but lack the necessary methods for example arguments. For example we want to find if a list of arguments passed in contain letter “b”. Since we can’t simply call .filter() on arguments because it is not an array, we have to convert it into an array by borrowing the .slice method from Array.prototype, and setting this context to the arguments object:

function findB() {
var args = Array.prototype.slice.call(arguments)
return args.filter(a => a.includes('b'))
}

findO("bud", "bid", "bed", "bad")
=> [ 'bed' ]

That’s so much of it!

Happy Reading!

--

--