如何编写一个接受可变数量参数的Javascript函数,并将所有这些参数转发给其他匿名函数?
例如,考虑一个触发事件的方法的场景:
function fireStartedEvent(a,b,c,d,e,f,g,...) {
for(var i = 0; i < startedListeners.length; i++) {
startedListeners[i](a,b,c,d,e,f,g,...);
}
}
特别是因为我有一个生成这些fire方法的事件工厂,所以这些方法无需知道给定事件或其处理程序消耗了多少参数。所以我现在在7点硬连线(a到g)。如果它没有,没问题。如果它已经存在,它们会被切断。我怎样才能捕获并传递所有参数?
感谢。
(在这里使用jQuery或任何其他Javascript框架不是一个选项。)
答案 0 :(得分:8)
解决这个问题需要了解两个JavaScript概念。
第一个是特殊的arguments
局部变量,可以用来访问函数参数而不知道它们的名字,它就像一个数组。但是,arguments
不是 Array
,而是“数组之类的” - 具有名为0..n-1
的属性,其中n
是数字函数的参数和length
属性 - 对象。一个简单的演示用法可能是:
function f (a) { // can include names still, if desired
// arguments instanceof Array -> false (exceptions to this?)
var firstArg = arguments[0]
// a === firstArg -> always true
// iterate all arguments, just as if it were an Array:
for (var i = 0; i < arguments.length; i++) {
alert(i + " : " + arguments[i])
}
}
f("a","b","c")
第二个特性是Function.apply
,它将调用具有特定上下文的函数(调用它时为this
),其参数来自“数组”的扩展喜欢“对象。 但请参阅 1 。
因此,把它放在一起:
function fireStartedEvent() {
for(var i = 0; i < startedListeners.length; i++) {
// jQuery will often pass in "cute" things, such as a element clicked
// as the context. here we just pass through the current context, `this`,
// as well as the arguments we received.
var arg = Array.prototype.slice.call(arguments)
startedListeners[i].apply(this, args)
}
}
1 虽然ECMAScript规范只需要一个“类似”数组的对象,但Function.apply
不会普遍使用“数组类似”对象和常见实现的数量需要一个正确的Array
对象。来自Function.apply
链接的警告:
注意:大多数浏览器,包括 Chrome 14 和 Internet Explorer 9 ,仍然不接受类似对象的数组,并会抛出异常[如果是非传递数组对象]。 [FireFox已在版本4中修复。]
值得庆幸的是,有一个相对简单的习惯用法将“数组类似”对象转换为Array
(这具有讽刺意味,因为Array.slice
通常使用“数组”喜欢“对象”:
var args = Array.prototype.slice.call(arguments);
(然后args
可以普遍使用Function.apply
。)
答案 1 :(得分:5)
我认为“apply”和“arguments”是你可以在这里使用的两个JavaScript概念:
function fireStartedEvent() {
for (var i = 0; i < startedListeners.length; i++) {
startedListeners[i].apply(startedListeners[i], arguments);
}
}
这是我的Firebug控制台中的一些代码,我试过这个代码:
a = function(foo) { alert('a: ' + foo); };
b = function(foo, bar) { alert('b: ' + foo + ', ' + bar); };
startedListeners = [a, b];
function fireStartedEvent() {
for (var i = 0; i < startedListeners.length; i++) {
startedListeners[i].apply(startedListeners[i], arguments);
}
}
fireStartedEvent('one', 'two');