电话和申请有什么区别?

时间:2009-12-31 19:56:26

标签: javascript performance function dynamic

使用callapply调用函数有什么区别?

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

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

上述两种方法之间是否存在性能差异?何时最好使用call而不是apply,反之亦然?

25 个答案:

答案 0 :(得分:3472)

不同之处在于apply允许您使用arguments作为数组调用函数; call要求显式列出参数。一个有用的助记符是 A a rray和 C c omma。”

请参阅有关applycall的MDN文档。

伪语法:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

从ES6开始,还有spread数组用于call函数的可能性,您可以看到兼容性here

示例代码:

function theFunction(name, profession) {
    console.log("My name is " + name + " and I am a " + profession +".");
}
theFunction("John", "fireman");
theFunction.apply(undefined, ["Susan", "school teacher"]);
theFunction.call(undefined, "Claude", "mathematician");
theFunction.call(undefined, ...["Matthew", "physicist"]); // used with the spread operator

答案 1 :(得分:218)

ķ。斯科特艾伦有a nice writeup的问题。

基本上,它们在处理函数参数方面有所不同。

  

apply()方法与call()相同,但apply()需要数组作为第二个参数。数组表示目标方法的参数。“

所以:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);

答案 2 :(得分:152)

要回答有关何时使用每个函数的部分,如果您不知道要传递的参数数量,或者它们是否已经在数组或类数组对象中,请使用apply(如arguments对象转发自己的参数。否则使用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

当我没有传递任何参数时(例如你的例子),我更喜欢call,因为我调用函数。 apply意味着您函数应用于(不存在的)参数。

应该没有任何性能差异,除非您使用apply并将参数包装在数组中(例如f.apply(thisObject, [a, b, c])而不是f.call(thisObject, a, b, c))。我没有对它进行过测试,因此可能存在差异,但它会特定于浏览器。如果你没有数组中的参数,那么call可能会更快,如果你的话,apply会更快。

答案 3 :(得分:105)

这是一个很好的助记符。 A pply使用 A 数组, A 总是需要一个或两个参数。当你使用 C 时,你必须 C 计算参数的数量。

答案 4 :(得分:91)

虽然这是一个古老的话题,但我只想指出.call比.apply略快。我无法确切地告诉你原因。

参见jsPerf,http://jsperf.com/test-call-vs-apply/3


[UPDATE!]

Douglas Crockford简要提到了两者之间的差异,这可能有助于解释性能差异...... http://youtu.be/ya4UHuXNygM?t=15m52s

Apply接受一组参数,而Call接受零个或多个单独的参数!啊哈!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)

答案 5 :(得分:75)

关注Closure: The Definitive Guide by Michael Bolin的摘录。它可能看起来有点冗长,但它充满了很多洞察力。从“附录B.经常误解的JavaScript概念”:


调用函数时this指的是什么

当调用foo.bar.baz()形式的函数时,对象foo.bar被称为接收者。调用该函数时,接收器用作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);

如果在调用函数时没有显式接收器,则全局对象成为接收器。如第47页的“goog.global”中所述,窗口是在Web浏览器中执行JavaScript时的全局对象。这会导致一些令人惊讶的行为:

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

即使obj.addValuesf引用相同的函数,它们在调用时的行为也不同,因为每次调用时接收器的值都不同。因此,在调用引用this的函数时,确保this在调用时具有正确的值非常重要。需要明确的是,如果函数体中未引用this,那么f(20)obj.addValues(20)的行为将是相同的。

因为函数是JavaScript中的第一类对象,所以它们可以有自己的方法。所有函数都具有方法call()apply(),这使得在调用函数时可以重新定义接收器(即this引用的对象)。方法签名如下:

/**
* @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;

请注意,call()apply()之间的唯一区别是call()将函数参数作为单个参数接收,而apply()将它们作为单个数组接收:

// 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]);

以下调用是等效的,因为fobj.addValues引用相同的函数:

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

但是,由于call()apply()都没有使用其自己的接收器的值来替换未指定的接收器参数,因此以下内容不起作用:

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

调用函数时,this的值永远不能为nullundefined。当nullundefined作为call()apply()的接收方提供时,全局对象将用作接收方的值。因此,前面的代码具有向全局对象添加名为value的属性的不良副作用。

将函数视为不知道它所分配的变量可能会有所帮助。这有助于强化这样一种观点,即在调用函数时它将被绑定,而不是在定义它时。


提取物结束。

答案 6 :(得分:33)

有时一个对象借用另一个对象的函数很有用,这意味着借用对象只是执行lent函数,就像它自己一样。

一个小代码示例:

var friend = {
    car: false,
    lendCar: function ( canLend ){
      this.car = canLend;
 }

}; 

var me = {
    car: false,
    gotCar: function(){
      return this.car === true;
  }
};

console.log(me.gotCar()); // false

friend.lendCar.call(me, true); 

console.log(me.gotCar()); // true

friend.lendCar.apply(me, [false]);

console.log(me.gotCar()); // false

这些方法对于为对象提供临时功能非常有用。

答案 7 :(得分:25)

Call,Apply和Bind的另一个例子。 Call和Apply之间的区别很明显,但 Bind 的工作原理如下:

  1. Bind返回可以执行的函数的实例
  2. 第一个参数是' this '
  3. 第二个参数是逗号分隔参数列表(如 Call
  4. }

    function Person(name) {
        this.name = name; 
    }
    Person.prototype.getName = function(a,b) { 
         return this.name + " " + a + " " + b; 
    }
    
    var reader = new Person('John Smith');
    
    reader.getName = function() {
       // Apply and Call executes the function and returns value
    
       // Also notice the different ways of extracting 'getName' prototype
       var baseName = Object.getPrototypeOf(this).getName.apply(this,["is a", "boy"]);
       console.log("Apply: " + baseName);
    
       var baseName = Object.getPrototypeOf(reader).getName.call(this, "is a", "boy"); 
       console.log("Call: " + baseName);
    
       // Bind returns function which can be invoked
       var baseName = Person.prototype.getName.bind(this, "is a", "boy"); 
       console.log("Bind: " + baseName());
    }
    
    reader.getName();
    /* Output
    Apply: John Smith is a boy
    Call: John Smith is a boy
    Bind: John Smith is a boy
    */
    

答案 8 :(得分:23)

我想展示一个例子,其中使用'valueForThis'参数:

Array.prototype.push = function(element) {
   /*
   Native code*, that uses 'this'       
   this.put(element);
   */
}
var array = [];
array.push(1);
array.push.apply(array,[2,3]);
Array.prototype.push.apply(array,[4,5]);
array.push.call(array,6,7);
Array.prototype.push.call(array,8,9);
//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

**详情:http://es5.github.io/#x15.4.4.7 *

答案 9 :(得分:20)

Call()采用逗号分隔的参数,例如:

.call(scope, arg1, arg2, arg3)

和apply()接受一组参数,例如:

.apply(scope, [arg1, arg2, arg3])

这里有几个用法示例: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/

答案 10 :(得分:19)

来自the MDN docs on Function.prototype.apply()

  

apply()方法调用具有给定this值的函数   作为数组(或类数组对象)提供的参数。

     

语法

fun.apply(thisArg, [argsArray])

来自the MDN docs on Function.prototype.call()

  

call()方法调用具有给定this值的函数和单独提供的参数。

     

语法

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

来自Function.apply and Function.call in JavaScript

  

apply()方法与call()相同,但apply()需要一个   数组作为第二个参数。数组表示的参数   目标方法。

代码示例:

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'
));

另见this Fiddle

答案 11 :(得分:11)

基本区别在于call()接受 参数列表 ,而apply()接受 单个参数数组< / EM>

答案 12 :(得分:10)

这是一篇小帖子,我在上面写道:

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"

答案 13 :(得分:7)

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

答案 14 :(得分:6)

我们可以区分呼叫和应用方法,如下所示

CALL:具有参数的函数单独提供。 如果你知道要传递的参数或者没有传递参数,你可以使用call。

APPLY:使用作为数组提供的参数调用函数。如果您不知道有多少参数将传递给函数,则可以使用apply。

使用apply over call有一个优点,我们不需要更改参数的数量,只有我们可以更改传递的数组。

性能没有太大差异。但是我们可以说调用比应用更快一点,因为数组需要在apply方法中进行评估。

答案 15 :(得分:5)

这些方法之间的区别在于,您希望如何传递参数。

“A代表数组,C代表逗号”是一个方便的助记符。

答案 16 :(得分:5)

调用和应用两者用于在执行函数时强制this值。唯一的区别是call需要n+1个参数,其中1是this'n' argumentsapply只接受两个参数,一个是this,另一个是参数数组。

我在apply call上看到的优势是我们可以轻松地将函数调用委托给其他函数;

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

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

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

观察我们使用hello轻松将sayHello委托给apply,但使用call这很难实现。

答案 17 :(得分:4)

尽管callapply实现同样的目标,但我认为至少有一个地方您无法使用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

答案 18 :(得分:4)

主要区别在于,使用call,我们可以正常更改范围和传递参数,但apply允许您使用参数作为Array调用它(将它们作为数组传递)。但就你在代码中的作用而言,它们非常相似。

  

虽然这个函数的语法几乎与   apply(),根本区别在于call()接受一个参数   list,而apply()接受一个参数数组。

正如您所看到的,没有太大的区别,但仍有一些情况我们更喜欢使用call()或apply()。例如,查看下面的代码,它使用apply方法从MDN中查找数组中的最小和最大数字:

// min/max number in an array
var numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
var max = Math.max.apply(null, numbers); 
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)

var min = Math.min.apply(null, numbers)

所以主要区别在于我们传递论据的方式:

致电:

function.call(thisArg, arg1, arg2, ...);

申请:

function.apply(thisArg, [argsArray]);

答案 19 :(得分:3)

摘要:

call()apply()都是位于Function.prototype上的方法。因此,它们可通过原型链在每个功能对象上使用。 call()apply()都可以执行具有指定值this的函数。

call()apply()之间的主要区别是必须将参数传递到其中的方式。在call()apply()中,您都将作为值的对象作为第一个参数传递为this。其他参数在以下方面有所不同:

  • 使用call(),您必须正常输入参数(从第二个参数开始)
  • 使用apply(),您必须传递参数数组。

示例:

let obj = {
  val1: 5,
  val2: 10
}

const summation = function (val3, val4) {
  return  this.val1 + this.val2 + val3 + val4;
}

console.log(summation.apply(obj, [2 ,3]));
// first we assign we value of this in the first arg
// with apply we have to pass in an array


console.log(summation.call(obj, 2, 3));
// with call we can pass in each arg individually

我为什么需要使用这些功能?

在JavaScript中,this值有时可能很棘手。 {strong>在执行函数时而不是在定义函数时确定的值this如果我们的函数依赖于正确的this绑定,则可以使用call()apply()来强制执行此行为。例如:

var name = 'unwantedGlobalName';

const obj =  {
  name: 'Willem',
  sayName () { console.log(this.name);}
}


let copiedMethod = obj.sayName;
// we store the function in the copiedmethod variable



copiedMethod();
// this is now window, unwantedGlobalName gets logged

copiedMethod.call(obj);
// we enforce this to be obj, Willem gets logged

答案 20 :(得分:1)

让我为此添加一些细节。

这两个电话几乎相等:

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

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

只有很小的区别:

  
      
  • spread运算符...允许传递 可迭代 args作为要调用的列表。
  •   
  • apply仅接受 类数组 args。
  •   

因此,这些调用相互补充。在我们期望可迭代的地方,call起作用,在我们期望类似数组的apply起作用。

对于可迭代类似于数组的对象,就像真实数组一样,从技术上讲,我们可以使用其中的任何对象,但 可能会更快,因为大多数JavaScript引擎在内部对其进行了优化。

答案 21 :(得分:1)

call() 它是 javascript 中的预定义方法。 此方法通过指定所有者对象来调用方法(函数)。

  function sayHello(){
  return "Hello " + this.name;
}
        
var obj = {name: "Sandy"};
        
sayHello.call(obj);
        
// Returns "Hello Sandy"

调用接受参数

function saySomething(message){
  return this.name + " is " + message;
}
        
var person4 = {name:  "John"};
        
saySomething.call(person4, "awesome");
// Returns "John is awesome"    

apply() apply 方法类似于 call() 方法。唯一不同的是, call() 方法分别接受参数,而 apply() 方法将参数作为数组。

示例

function saySomething(message){
  return this.name + " is " + message;
}
        
var person4 = {name:  "John"};
        
saySomething.apply(person4, ["awesome"]);

答案 22 :(得分:0)

flatline解释的很好。我只想添加一个简单的示例。这对于初学者来说很容易理解。

func.call(context, args1 , args2 ); // pass arguments as "," saprated value

func.apply(context, [args1 , args2 ]);   //  pass arguments as "Array"

我们还使用“调用”和“应用”方法来更改以下代码中定义的引用

    let Emp1 = {
        name: 'X',
        getEmpDetail: function (age, department) {
            console.log('Name :', this.name, '  Age :', age, '  Department :', department)
        }
    }
    Emp1.getEmpDetail(23, 'Delivery')

    // 1st approch of chenging "this"
    let Emp2 = {
        name: 'Y',
        getEmpDetail: Emp1.getEmpDetail
    }
    Emp2.getEmpDetail(55, 'Finance')

    // 2nd approch of changing "this" using "Call" and "Apply"
    let Emp3 = {
        name: 'Z',
    }

    Emp1.getEmpDetail.call(Emp3, 30, 'Admin')        
// here we have change the ref from **Emp1 to Emp3**  object
// now this will print "Name =  X" because it is pointing to Emp3 object
    Emp1.getEmpDetail.apply(Emp3, [30, 'Admin']) //

答案 23 :(得分:0)

call()方法调用具有给定this值和参数的函数。enter image description here

apply()- 与call()方法类似,apply()方法中的第一个参数设置this值,该值是在其上调用函数的对象。在这种情况下,它就是上面的obj对象。 apply()call()方法之间的唯一区别是apply()方法的第二个参数将实际函数的参数接受为数组。 enter image description here

答案 24 :(得分:0)

以相同的方式调用和应用。当我们使用call and apply时,它将立即调用。

调用和应用都将“ this”参数作为第一个参数,而第二个参数只是不同。

该调用将函数的参数作为列表(逗号) Apply将函数的参数作为数组。

您可以在下面的youtube视频中找到绑定,调用和应用之间的完全区别。

https://www.youtube.com/watch?v=G-EfxnG0DtY&t=180s