The Use and Difference of call and apply in JavaScript

  • 2021-07-10 18:16:05
  • OfStack

apply takes two arguments. The first parameter specifies the point of the this object in the function body. The second parameter is a subscript collection, which can be an array or a class array. The apply method passes the elements in this collection as parameters to the called function:


var func = function( a, b, c ){
  alert ( [ a, b, c ] ); //  Output  [ 1, 2, 3 ]
};
func.apply( null, [ 1, 2, 3 ] );

In this code, arguments 1, 2, and 3 are placed in an array and passed into the func function, which correspond to a, b, and c in the func parameter list, respectively.

The number of parameters passed in by call is not fixed. Like apply, the first parameter also represents the this point in the function body. From the second parameter, each parameter is passed into the function in turn:


var func = function( a, b, c ){
  alert ( [ a, b, c ] ); //  Output  [ 1, 2, 3 ]
};
func.call( null, 1, 2, 3 );

When calling a function, the interpreter of JavaScript doesn't care about the difference in number, type and order between formal parameters and arguments. The parameters of JavaScript are represented by an array internally. In this sense, the utilization rate of apply is higher than that of call, so we don't have to care how many parameters are passed into the function, just push it with apply 1. call is a grammatical sugar wrapped on apply. If we know exactly how many parameters a function accepts and want to express the corresponding relationship between formal parameters and actual parameters clearly, we can also use call to transmit parameters.

Use of call and apply

1. Change the direction of this

The most common use of call and apply is to change the this pointing inside a function. Let's look at an example:


var obj1 = {
  name: 'sven'
};
var obj2 = {
  name: 'anne'
};
window.name = 'window';
var getName = function(){
  alert ( this.name );
};
getName(); //  Output : window
getName.call( obj1 ); //  Output : sven
getName.call( obj2 ); //  Output : anne

When the code getName. call (obj1) is executed, the this in the body of the getName function points to the obj1 object, so the


var getName = function(){
alert ( this.name );
};

Actually equivalent to:


var getName = function(){
alert ( obj1.name ); //  Output : sven
};

In actual development, it is often encountered that this points to a scene that has been inadvertently changed. For example, there is an div node, and this in onclick event of div node originally points to this div:


document.getElementById( 'div1' ).onclick = function(){
  alert( this.id ); //  Output: div1
};

If there is an internal function func in the event function, when the func function is called inside the event, this in the func function body points to window instead of div as we expected, as shown in the following code:


document.getElementById( 'div1' ).onclick = function(){
  alert( this.id ); //  Output: div1
  var func = function(){
    alert ( this.id ); //  Output: undefined
  }
  func();
};

At this time, we use call to correct this in func function so that it still points to div:


document.getElementById( 'div1' ).onclick = function(){
  var func = function(){
    alert ( this.id ); //  Output: div1
  }
  func.call( this );
};

2. Function.prototype.bind

Most advanced browsers have implemented the built-in Function. prototype. bind, which is used to specify the this point inside the function. Even if there is no native Function. prototype. bind implementation, it is not difficult for us to simulate one. The code is as follows:


Function.prototype.bind = function( context ){
var self = this; //  Save the original function 
return function(){ //  Return 1 A new function 
    return self.apply( context, arguments ); //  When a new function is executed, it will     Put the previously passed in context
  //  As in the new function body this
  }
};
var obj = {
  name: 'sven'
};
var func = function(){
  alert ( this.name ); //  Output: sven
}.bind( obj);
func();

We "wrap" the func function through Function. prototype. bind, and pass in an object context as an argument, which is the this object we want to correct.

In the internal implementation of Function. prototype. bind, we first save the reference to the func function and then return a new function. When we execute the func function in the future, we actually execute the new function just returned first. Inside the new function, the code self. apply (context, arguments) executes the original func function and specifies the context object as this in the func function body.

This is a simplified implementation of Function. prototype. bind, and we usually make it a little more complicated.

So that one parameter can be pre-filled in the func function:


Function.prototype.bind = function(){
  var self = this, //  Save the original function 
  context = [].shift.call( arguments ), //  To be bound this  Context 
  args = [].slice.call( arguments ); //  The remaining parameters are converted to an array 
  return function(){ //  Return 1 A new function 
    return self.apply( context, [].concat.call( args, [].slice.call(  arguments ) ) );
    //  When a new function is executed, the previously passed in context  As in the new function body this
    //  And the parameters passed in twice are combined as the parameters of the new function 
  }
};
var obj = {
  name: 'sven'
};
var func = function( a, b, c, d ){
  alert ( this.name ); //  Output: sven
  alert ( [ a, b, c, d ] ) //  Output: [ 1, 2, 3, 4 ]
}.bind( obj, 1, 2 );
func( 3, 4 );

Related articles: