使用Function.pr原型.apply()和Function.pr原型.call()调用函数有什么区别?
var func = function() {
alert('hello!');
};
函数.apply();vs func.call();
上述两种方法之间是否存在性能差异?何时最好使用呼叫转移应用,反之亦然?
使用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
其他回答
调用、应用和绑定的另一个示例。Call和Apply之间的区别很明显,但Bind的工作原理如下:
Bind返回可以执行的函数的实例第一个参数是“this”第二个参数是逗号分隔的参数列表(如Call)
}
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
*/
要回答有关何时使用每个函数的问题,如果您不知道要传递的参数的数量,或者它们已经在数组或类似数组的对象中(例如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))。我还没有测试过它,所以可能会有差异,但它会非常特定于浏览器。如果数组中没有参数,则调用可能会更快,如果有,则应用可能会更快。
不同之处在于,call()分别接受函数参数,而apply()接受数组中的函数参数。
区别在于apply允许您以数组的形式调用带有参数的函数;调用要求显式列出参数。一个有用的助记符是“A代表数组,C代表逗号。”
请参阅MDN的申请和通话文档。
伪语法:
函数.应用(值For This,arrayOfArgs)
函数调用(此值,arg1,arg2,…)
从ES6开始,还可以扩展数组以与调用函数一起使用,您可以在这里看到兼容性。
示例代码:
函数函数(名称、专业){console.log(“我的名字是”+name+“,我是”+profession+“。”);}函数(“John”,“fireman”);函数.应用(未定义,[“Susan”,“学校老师”]);函数调用(未定义,“克劳德”,“数学家”);函数调用(未定义,…[“Matthew”,“物理学家”]);//与排列运算符一起使用
以下摘自Michael Bolin的《结束:最终指南》。它可能看起来有点冗长,但它充满了很多洞察力。摘自“附录B.经常误解的JavaScript概念”:
调用函数时所指的内容
当调用foo.bar.baz()形式的函数时,对象foo.bar被称为接收器。当函数被调用时,接收器被用作此函数的值:
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时,window是全局对象。这导致了一些令人惊讶的行为:
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.addValues和f引用同一个函数,但它们在调用时的行为不同,因为每次调用中接收方的值都不同。因此,在调用引用此的函数时,务必确保在调用此函数时具有正确的值。要清楚的是,如果函数体中没有引用这一点,那么f(20)和obj.addValues(20)的行为将是相同的。
因为函数是JavaScript中的一流对象,所以它们可以有自己的方法。所有函数都有方法call()和apply(),这使得在调用函数时可以重新定义接收器(即它所指的对象)。方法签名如下:
/**
* @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]);
以下调用是等效的,因为f和obj.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的值不能为null或未定义。当向call()或apply()提供null或undefined作为接收方时,将使用全局对象作为接收方的值。因此,前面的代码具有相同的不良副作用,即向全局对象添加一个名为value的属性。
将函数视为不知道其所分配的变量可能会有所帮助。这有助于强化这样一种观念,即当调用函数时,而不是定义函数时,它的值将被绑定。
提取结束。