What is the difference between call and apply?

前端 未结 24 1700
太阳男子
太阳男子 2020-11-21 07:12

What is the difference between using call and apply to invoke a function?

var func = function() {
  alert(\'hello!\');
};


        
相关标签:
24条回答
  • 2020-11-21 07:55

    From the MDN docs on Function.prototype.apply() :

    The apply() method calls a function with a given this value and arguments provided as an array (or an array-like object).

    Syntax

    fun.apply(thisArg, [argsArray])
    

    From the MDN docs on Function.prototype.call() :

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

    Syntax

    fun.call(thisArg[, arg1[, arg2[, ...]]])
    

    From Function.apply and Function.call in JavaScript :

    The apply() method is identical to call(), except apply() requires an array as the second parameter. The array represents the arguments for the target method.


    Code example :

    var doSomething = function() {
        var arr = [];
        for(i in arguments) {
            if(typeof this[arguments[i]] !== 'undefined') {
                arr.push(this[arguments[i]]);
            }
        }
        return arr;
    }
    
    var output = function(position, obj) {
        document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\n<br>\n<br><hr>';
    }
    
    output(1, doSomething(
        'one',
        'two',
        'two',
        'one'
    ));
    
    output(2, doSomething.apply({one : 'Steven', two : 'Jane'}, [
        'one',
        'two',
        'two',
        'one'
    ]));
    
    output(3, doSomething.call({one : 'Steven', two : 'Jane'},
        'one',
        'two',
        'two',
        'one'
    ));

    See also this Fiddle.

    0 讨论(0)
  • 2020-11-21 07:56

    The difference is that call() takes the function arguments separately, and apply() takes the function arguments in an array.

    0 讨论(0)
  • 2020-11-21 07:57

    Follows an extract from Closure: The Definitive Guide by Michael Bolin. It might look a bit lengthy, but it's saturated with a lot of insight. From "Appendix B. Frequently Misunderstood JavaScript Concepts":


    What this Refers to When a Function is Called

    When calling a function of the form foo.bar.baz(), the object foo.bar is referred to as the receiver. When the function is called, it is the receiver that is used as the value for this:

    var obj = {};
    obj.value = 10;
    /** @param {...number} additionalValues */
    obj.addValues = function(additionalValues) {
      for (var i = 0; i < arguments.length; i++) {
        this.value += arguments[i];
      }
      return this.value;
    };
    // Evaluates to 30 because obj is used as the value for 'this' when
    // obj.addValues() is called, so obj.value becomes 10 + 20.
    obj.addValues(20);
    

    If there is no explicit receiver when a function is called, then the global object becomes the receiver. As explained in "goog.global" on page 47, window is the global object when JavaScript is executed in a web browser. This leads to some surprising behavior:

    var f = obj.addValues;
    // Evaluates to NaN because window is used as the value for 'this' when
    // f() is called. Because and window.value is undefined, adding a number to
    // it results in NaN.
    f(20);
    // This also has the unintentional side effect of adding a value to window:
    alert(window.value); // Alerts NaN
    

    Even though obj.addValues and f refer to the same function, they behave differently when called because the value of the receiver is different in each call. For this reason, when calling a function that refers to this, it is important to ensure that this will have the correct value when it is called. To be clear, if this were not referenced in the function body, then the behavior of f(20) and obj.addValues(20) would be the same.

    Because functions are first-class objects in JavaScript, they can have their own methods. All functions have the methods call() and apply() which make it possible to redefine the receiver (i.e., the object that this refers to) when calling the function. The method signatures are as follows:

    /**
    * @param {*=} receiver to substitute for 'this'
    * @param {...} parameters to use as arguments to the function
    */
    Function.prototype.call;
    /**
    * @param {*=} receiver to substitute for 'this'
    * @param {Array} parameters to use as arguments to the function
    */
    Function.prototype.apply;
    

    Note that the only difference between call() and apply() is that call() receives the function parameters as individual arguments, whereas apply() receives them as a single array:

    // When f is called with obj as its receiver, it behaves the same as calling
    // obj.addValues(). Both of the following increase obj.value by 60:
    f.call(obj, 10, 20, 30);
    f.apply(obj, [10, 20, 30]);
    

    The following calls are equivalent, as f and obj.addValues refer to the same function:

    obj.addValues.call(obj, 10, 20, 30);
    obj.addValues.apply(obj, [10, 20, 30]);
    

    However, since neither call() nor apply() uses the value of its own receiver to substitute for the receiver argument when it is unspecified, the following will not work:

    // Both statements evaluate to NaN
    obj.addValues.call(undefined, 10, 20, 30);
    obj.addValues.apply(undefined, [10, 20, 30]);
    

    The value of this can never be null or undefined when a function is called. When null or undefined is supplied as the receiver to call() or apply(), the global object is used as the value for receiver instead. Therefore, the previous code has the same undesirable side effect of adding a property named value to the global object.

    It may be helpful to think of a function as having no knowledge of the variable to which it is assigned. This helps reinforce the idea that the value of this will be bound when the function is called rather than when it is defined.


    End of extract.

    0 讨论(0)
  • 2020-11-21 07:57

    Fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments.

    0 讨论(0)
  • 2020-11-21 07:59

    Even though call and apply achive the same thing, I think there is atleast one place where you cannot use call but can only use apply. That is when you want to support inheritance and want to call the constructor.

    Here is a function allows you to create classes which also supports creating classes by extending other classes.

    function makeClass( properties ) {
        var ctor = properties['constructor'] || function(){}
        var Super = properties['extends'];
        var Class = function () {
                     // Here 'call' cannot work, only 'apply' can!!!
                     if(Super)
                        Super.apply(this,arguments);  
                     ctor.apply(this,arguments);
                    }
         if(Super){
            Class.prototype = Object.create( Super.prototype );
            Class.prototype.constructor = Class;
         }
         Object.keys(properties).forEach( function(prop) {
               if(prop!=='constructor' && prop!=='extends')
                Class.prototype[prop] = properties[prop];
         });
       return Class; 
    }
    
    //Usage
    var Car = makeClass({
                 constructor: function(name){
                             this.name=name;
                            },
                 yourName: function() {
                         return this.name;
                       }
              });
    //We have a Car class now
     var carInstance=new Car('Fiat');
    carInstance.youName();// ReturnsFiat
    
    var SuperCar = makeClass({
                   constructor: function(ignore,power){
                         this.power=power;
                      },
                   extends:Car,
                   yourPower: function() {
                        return this.power;
                      }
                  });
    //We have a SuperCar class now, which is subclass of Car
    var superCar=new SuperCar('BMW xy',2.6);
    superCar.yourName();//Returns BMW xy
    superCar.yourPower();// Returns 2.6
    
    0 讨论(0)
  • 2020-11-21 08:01

    Difference between these to methods are, how you want to pass the parameters.

    “A for array and C for comma” is a handy mnemonic.

    0 讨论(0)
提交回复
热议问题