Indeed, defining the method on the object will be viable alternative in many cases. However, sometimes it is not an option:
1. The object does not allow the method to be defined:
"use strict";
function showDetails() { console.log(this.name); }
var obj = Object.seal({ name: 'Strawberry' });
showDetails.call(obj); // Strawberry
obj.showDetails = showDetails; // fails
2. Object already has a property with that name
function name() { console.log(this.name) }
var obj = Object.seal({ name: 'Strawberry' });
name.call(obj); // Strawberry
obj.name = name;
obj.name(); // does not show 'Strawberry', but the function
It could also fail if the said property is read-only (with defineProperty
and writable: false
).
3. Adding the property influences the return value
Suppose the function performs self inspection, e.g. it counts the number of properties it has:
function memberCount() { return Object.keys(this).length; };
var obj = { name: 'Strawberry' };
console.log(memberCount.call(obj)); // 1
obj.count = memberCount;
console.log(obj.count()); // 2
4. Adding the property influences the remaining code
This is especially a problem with objects of which you expect a certain behaviour, like arrays:
function first() { return this[0]; };
var arr = ['Strawberry', 'Banana'];
console.log(first.call(arr)); // Strawberry
arr.first = first;
// ...
for (var i in arr) { // Iterate through array
if (arr.hasOwnProperty(i)) {
console.log(arr[i]); // Oops, the function appears also.
}
}
Other examples for apply
and bind
There are other examples where the use of apply
or bind
is useful in case the target object already has the method:
apply
may be used with methods that require separate arguments, while you just have an array of values. In ES6 you can use the spread syntax to overcome this, but traditionally, apply
was the solution for this.
For example, to get the minimum value in an array:
var arr = [5,3,1,6,2];
console.log(Math.min.apply(Math, arr));
The bind
method is often used to pass a method as a callback, but you need it to be bound to your object:
var obj = {
flavour: 'Strawberry',
smell: function () { console.log(this.flavour) }
};
window.addEventListener('load', obj.smell); // ......> undefined
window.addEventListener('load', obj.smell.bind(obj)); // .....>Strawberry
ES6 Considerations
Where this
must be bound, there can still be use for these methods. But with the arrow function syntax, this
keeps its lexical value, and passing the this
argument to apply
, call
or bind
on such function has no effect. Often it also becomes unnecessary, as that lexical this
is often exactly what is needed.
Take the example of getting the minimum value of an array. With ES6 it can be written as follows:
const arr = [5,3,1,6,2,4];
console.log(Math.min(...arr));
Or take an example where a method (using this
) is passed as callback:
class Cls {
constructor() {
this.flavour = 'Strawberry'
this.smell = () => console.log(this.flavour)
}
};
window.addEventListener('load', (new Cls).smell); // ......> Strawberry