使用Function.pr原型.apply()和Function.pr原型.call()调用函数有什么区别?

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

函数.apply();vs func.call();

上述两种方法之间是否存在性能差异?何时最好使用呼叫转移应用,反之亦然?


当前回答

尽管call和apply实现了相同的功能,但我认为至少有一个地方不能使用call,只能使用apply。这是您希望支持继承并希望调用构造函数的时候。

这里有一个函数允许您创建类,它也支持通过扩展其他类来创建类。

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

其他回答

要回答有关何时使用每个函数的问题,如果您不知道要传递的参数的数量,或者它们已经在数组或类似数组的对象中(例如arguments对象),请使用apply来转发您自己的参数。否则请使用call,因为不需要将参数包装在数组中。

f.call(thisObject, a, b, c); // Fixed number of arguments

f.apply(thisObject, arguments); // Forward this function's arguments

var args = [];
while (...) {
    args.push(some_value());
}
f.apply(thisObject, args); // Unknown number of arguments

当我不传递任何参数时(如您的示例),我更喜欢调用,因为我在调用函数。apply将暗示您正在将函数应用于(不存在的)参数。

应该不会有任何性能差异,除非您使用apply并将参数包装在数组中(例如f.apply(thisObject,[a,b,c])而不是f.call(thisObject,a,b,c))。我还没有测试过它,所以可能会有差异,但它会非常特定于浏览器。如果数组中没有参数,则调用可能会更快,如果有,则应用可能会更快。

让我补充一点细节。

这两个调用几乎相等:

func.call(context, ...args); // pass an array as list with spread operator

func.apply(context, args);   // is same as using apply

只有一个微小的区别:

排列运算符。。。允许传递可迭代参数作为要调用的列表。apply只接受类似数组的参数。

因此,这些呼叫是相辅相成的。在我们期望可迭代的地方,调用工作,在我们期望类似数组的地方,应用工作。

对于既可迭代又类似于数组的对象,比如真正的数组,我们技术上可以使用它们中的任何一个,但apply可能会更快,因为大多数JavaScript引擎内部对其进行了更好的优化。

这里有一个小帖子,我在上面写道:

http://sizeableidea.com/call-versus-apply-javascript/

var obj1 = { which : "obj1" },
obj2 = { which : "obj2" };

function execute(arg1, arg2){
    console.log(this.which, arg1, arg2);
}

//using call
execute.call(obj1, "dan", "stanhope");
//output: obj1 dan stanhope

//using apply
execute.apply(obj2, ["dan", "stanhope"]);
//output: obj2 dan stanhope

//using old school
execute("dan", "stanhope");
//output: undefined "dan" "stanhope"

调用和应用都用于在执行函数时强制此值。唯一的区别是调用采用n+1个参数,其中1是this和“n”个参数。apply只接受两个参数,一个是this,另一个是argument数组。

我在apply-over-call中看到的优点是,我们可以轻松地将函数调用委托给其他函数,而无需付出太多努力;

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

观察我们使用apply将hello委托给sayHello有多容易,但使用call很难做到这一点。

不同之处在于,call()分别接受函数参数,而apply()接受数组中的函数参数。