What is apply invocation pattern in Javascript in reference to function invocation patterns and how can I use it? What are the benefits of using this invocation pattern.
The use of apply
is related to the function context (the this
keyword) and argument passing.
First, I think you should know in which cases the this
keyword is implicitly set:
1- When a function is called as a method (the function is invoked as member of an object):
obj.method(); // 'this' inside method will refer to obj
2- A normal function call:
myFunction(); // 'this' inside the function will refer to the Global object
// or
(function () {})();
3- When the new
operator is used:
var obj = new MyObj(); // this will refer to a newly created object.
Here is when apply
and call
come, those methods let you set explicitly the context when you invoke a function, eg.:
function test(a) {
alert(this + a);
}
test.call('Hello', ' world!');
In the above code, when the test
function is called setting the this
keyword to a String ('Hello'
), and passing the a
argument (' world.'
).
Both, call
and apply
change the context of the executing function (the this
keyword) inside the called function, but the difference between them is that with apply
, you can send an Array or an Array-like object as the arguments of the function to be executed, which is extremely useful, for example:
function sum() {
var result = 0;
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
var args = [1,2,3];
sum.apply(null, args); // will return 6
That can avoid some very hacky, bad (and common) eval
calls like:
eval('sum(' + args.join() +')');
Another example, the Math.max
and Math.min
methods, those methods can receive an arbitrary number of arguments like:
var max = Math.max(2,4,6,8); // 8
But what if you have an Array of numbers?
var numbers = [2,4,6,8];
numbers.push(10);
var max = Math.max.apply(null, numbers); // 10
Also note that when null
or undefined
is used as the this
argument with call
or apply
, the this
object will refer to the Global object (similar to the case #2, normal function invocation).
For the Math.max
example, the context is not really relevant, since they are just like "static" methods, the this
keyword is not used internally...
I'm not aware of any design patterns named The Apply Pattern so I don't really think this is at all related to design patterns. However, there is an apply method of function objects in javascript (along with the corresponding call method) so I'll be explaining those.
The apply and call methods basically allow an object to "steal" a method from another object. You see, in javascript, methods are bound very-very late: at invocation time. Only when a method is called is the value of 'this'
resolved. In a normal method call:
some_object.do_something();
the 'this'
keyword in do_something refers to some_object. Apply and call lets you re-assign 'this'
. For example:
some_object.do_something.apply(another_object);
the 'this'
keyword in do_something now refers to another_object. So you're calling the do_something method belonging to some_object on another_object.
Now, this is interesting and all but why would anyone want to do this? Here's a concrete example of why this is useful:
// say you want to get some DIVs in the document, you can get all of them with:
var some_divs = document.getElementsByTagName('DIV');
// say you want the third to fifth DIV in the document, some_divs looks like an
// array so you might think you can slice it, but it's not. It's a collection
// of elements that fakes being an array and doesn't implement the slice method.
// No worries, we can steal the slice method from an array
// and apply it to some_divs:
var wanted_divs = [].slice.apply(some_divs,[2,5]);
// Alternatively:
var wanted_divs = [].slice.call(some_divs,2,5);
There is another use case for apply which is a result of the difference between how apply and call works. If you have all your arguments in an array and the function expects individual arguments you can use apply to pass the array and have the function see the content of the array as individual arguments:
function some_function (first,second) {
alert(first+second);
}
var argument_array = ['hello','world'];
some_function.apply(null, argument_array);
You can also use call/apply for inheritance.
function Client (id) {
this.id = id;
this.name = "Client" + id;
}
Client.prototype = {
constructor: Client
, toString: function () {
return this.name;
}
};
function WebClient (id) {
Client.call (this, id);
}
WebClient.prototype = new Client ();
WebClient.prototype.constructor = WebClient;
WebClient.prototype.ping = function () {
// do stuff
};
Notice Client.call (this, id);
This executes Client
using the this
instance that a new WebClient
would create. Thus when
this.id = id;
this.name = "Client" + id;
are executed inside Client
the WebClient
instance is getting assigned those properties.