In JavaScript a function or method is automatically assigned a member function, an example is the .toString() function which returns a string representation of the method. Both .call and .apply functions are member functions of the Function prototype. Below I’ll show the uses of both functions and hopefully explain how they work in the process.
OBJECT INHERITANCE
In this example, I’ll be creating a parent and child notation object. The parent contains a function that the child would need access to. There are different ways in which you can accomplish this by defining the function in the child object and¬†associating the parent function to it.
var parent = { name: '', getName: function( prefix ){ return prefix + ' ' + this.name; } } var child = { name: '', getName: parent.getName } child.name = 'child'; console.log( child.getName( 'Hello my name is' ) ); //prints 'Hello my name is child'
Doing it this way doesn’t really work well as you’ll have to define the parent’s public functions in the child object. Another way is by using the .call or .apply member function.
var parent = { name: '', getName: function( prefix ){ return prefix + ' ' + this.name; } } var child = { name: '' } console.log( parent.getName( 'Hello my name is' ) ); //prints 'Hello my name is' child.name = 'child'; var name = parent.getName.call( child, 'Hello my name is' ); console.log( name ); //prints 'Hello my name is child'
How this works is that .call replaces the object of a parent with the child object provided in the first argument and any argument after the first is passed to the function.
METHOD CHAINING
Another use is method chaining, an example is how the jQuery event handler works – the callback function object is always replaced with the selector object
var parent = { name: '', children: [], getName: function( prefix ){ return prefix + ' ' + this.name; }, bind: function( child, func ){ this.children.push( child ); func.call( this, child ); return this; } } var child = { name: '' } child.name = 'child'; parent.bind( child, function( child ){ console.log( this.children ); //prints [Object { name="child"}] console.log( child.name ) //prints 'child' });
As you can see the parent object is returned in the callback and also the child object which is passed back as an argument.
CALLING FUNCTIONS DYNAMICALLY
One of the main advantages of using the eval() function is to call functions dynamically, but an issue occurs when calling a method of an object.
var parent = { name: '', children: [], getName: function( prefix ){ return prefix + ' ' + this.name; }, bind: function( child, func ){ this.children.push( child ); eval( func )(); }, run: function(){ console.log( this.children ); } } var child = { name: '' } child.name = 'child'; parent.bind( child, 'parent.run' ); //prints undefined
The variable children is undefined because the object in the run function is actually the global object i.e window.children. This is where the .call and .apply functions come into play.
var parent = { name: '', children: [], getName: function( prefix ){ return prefix + ' ' + this.name; }, bind: function( child, func ){ this.children.push( child ); this.callFunction( func ); }, run: function(){ console.log( this.children ); }, callFunction: function( func ){ this[func].apply( this, Array.prototype.slice.call( arguments, 1 ) ); } } var child = { name: '' } child.name = 'child'; parent.bind( child, 'run' ); //prints [Object { name="child"}]
As this is associated with the notation object, you can create a standalone function to call functions dynamically as shown below:
function callFunction( func ) { return this[func].apply( this, Array.prototype.slice.call( arguments, 1 ) ); } function test( string ) { console.log( string ); } callFunction.call( parent, 'run' ); //prints [Object { name="child"}] callFunction( 'test', 'Hello world' ); //prints Hello world
Hopefully, these examples should explain how to use both functions.
DIFFERENCE BETWEEN .CALL AND .APPLY
Both functions behave the same way. The only difference is the way arguments are passed to the function. The .apply expects the argument to be an array of arguments, unlike the .call function where you can pass an argument list to it
function callFunction( func ) { return this[func].apply( this, Array.prototype.slice.call( arguments, 1 ) ); } function test( prefix, suffix ) { console.log( prefix + ' ' + suffix ); } callFunction.call( this, 'test', 'Hello', 'world' ); //prints 'Hello world' callFunction.apply( this, ['test', 'Hello', 'world'] ); //prints 'Hello world'