Есть ли способ получить только безымянные аргументы?

В функциях JavaScript arguments – это массив. подобный объект, содержащий все аргументы функции, независимо от того, именованы они или нет:

function f(foo, bar) {
    console.log(arguments);
}
f(1, '2', 'foo'); // [1, "2", "foo"]

Есть ли способ получить только неименованные аргументы, чтобы вы могли сделать что-то вроде этого?

function f(foo, bar) {
    console.log('foo:', foo, 'bar:', bar, 'the rest:', unnamedArguments);
}
f(1, '2', 'foo'); // foo: 1 bar: "2" the rest: ["foo"]

Но почему?

Реальный вариант использования — внедрение модулей Angular в качестве аргументов в модули RequireJS:

define([
    'angular',
    'myLibModule', // Exports an Angular module object
    'myOtherLibModule', // Exports an Angular module object
], function(angular, myLibModule, myOtherLibModule) {
    angular.module('MyApp', [myLibModule.name, myOtherLibModule.name]);
});

Поскольку список зависимостей модулей может быть довольно большим, это быстро становится очень громоздким. Хотя я мог бы решить это как

define([
    'angular',
    'underscore',
    'myLibModule', // Exports an Angular module object
    'myOtherLibModule', // Exports an Angular module object
], function(angular, _) {
    function angularModuleNames(modules) {
        return _.pluck(_.pick(modules, function(item) {
            return _.has(item, 'name');
        }), 'name');
    }
    angular.module('MyApp', angularModuleNames(arguments));
});

это также довольно громоздко, и было бы неплохо, если бы я мог вместо этого сделать что-то вроде этого:

define([
    'angular',
    'underscore',
    'myLibModule', // Exports an Angular module object
    'myOtherLibModule', // Exports an Angular module object
], function(angular, _) {
    angular.module('MyApp', _.pluck(unnamedArguments, 'name'));
});

Конечно, способ группировки зависимостей в RequireJS также достаточен для этого конкретного случая использования.


person Emil Lundberg    schedule 12.08.2014    source источник
comment
Функция ES6 для остаточных параметров (как в ... и остальные аргументы) делает именно то, что вы описываете в своем вопросе, но еще не получило широкого распространения. С помощью этой функции вы можете определить function f(foo, bar, ...unnamedArguments).   -  person apsillers    schedule 12.08.2014
comment
Верно. Python и Java (и, вероятно, многие другие языки) имеют эту функцию в течение длительного времени, поэтому было бы неплохо увидеть, как она появится и в JS.   -  person Emil Lundberg    schedule 12.08.2014
comment
@EmilLundberg TBH, в ES5 уже легко создавать вариативные функции, даже без длины функции: чаще всего вы знаете количество объявленных аргументов вашей функции.   -  person Denys Séguret    schedule 12.08.2014


Ответы (1)


Количество объявленных аргументов указывается в свойстве length функции.

Таким образом, вы можете получить аргументы, индекс которых больше или равен этому length :

var undeclaredArgs = [].slice.call(arguments, arguments.callee.length);

Поскольку вы не можете используйте arguments.callee в строгом режиме, начиная с ES5, вы должны использовать ссылку на функцию, когда это возможно:

var undeclaredArgs = [].slice.call(arguments, f.length);
person Denys Séguret    schedule 12.08.2014
comment
+1. Никогда не используйте arguments.callee, просто назовите выражение функции. - person Bergi; 12.08.2014
comment
Идеальный! Так что define([ ... ], function module(angular, _) { ... }) и Array.prototype.slice.call(arguments, module.length) - это то, что вам нужно. - person Emil Lundberg; 12.08.2014
comment
@RobG: Да, я знаю о них, но обычно игнорирую oldIE. По характеру ошибки она может привести к утечке некоторых объектов функций, но большая часть кода по-прежнему работает должным образом. - person Bergi; 12.08.2014