Как написать JS-функцию, которая принимает и “пересылает” переменное количество параметров?
-
06-07-2019 - |
Вопрос
Как мне написать функцию 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 здесь не рассматривается.)
Решение
Решение этой проблемы требует знания двух концепций 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
Ссылка:
Примечание: Большинство браузеров, включая Хром 14 и Internet Explorer 9, по-прежнему не принимают объекты, подобные массиву, и выдадут исключение [если передается объект, не являющийся массивом]. [FireFox был исправлен в версии 4.]
К счастью, существует относительно простая идиома для превращения объекта, подобного массиву, в Array
(что иронично , потому что Array.slice
делает универсально работать с объектом, подобным массиву):
var args = Array.prototype.slice.call(arguments);
(И тогда args
может быть универсально использован в быть использован в Function.apply
.)
Другие советы
Я думаю, что "применить" и "аргументы" - это две концепции 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');