Можно ли отправить переменное число аргументов в функцию JavaScript?

171

Можно ли отправить переменное число аргументов в функцию JavaScript из массива?

var arr = ['a','b','c']

var func = function()
{
    // debug 
    alert(arguments.length);
    //
    for(arg in arguments)
        alert(arg);
}

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'

Я недавно написал много Python, и это замечательный способ иметь возможность принимать varargs и отправлять их. например

def func(*args):
   print len(args)
   for i in args:
       print i

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(*arr) // prints 4 which is what I want, then 'a','b','c','d'

Можно ли в JavaScript отправить массив для обработки в качестве аргумента массива?

Огненная ворона
источник
4
Обратите внимание, что не рекомендуется использовать for - inцикл с argumentsобъектом - lengthвместо него следует использовать «нормальный» цикл, повторяющий свойство
Yi Jiang
2
это никогда не было проблемой, можете ли вы объяснить, почему это так? Объект arguments почти всегда достаточно мал, чтобы иметь незначительное улучшение производительности для использования версии agruments [0..length-1].
Огненная ворона
1
Я думаю, что arguments - это не фактический массив, а специальный объект, поэтому синтаксис «in» может не работать в зависимости от реализации JS.
О'Руни

Ответы:

209

Обновление : начиная с ES6, вы можете просто использовать синтаксис распространения при вызове функции:

func(...arr);

Поскольку ES6 также, если вы собираетесь обрабатывать свои аргументы как массив, вы также можете использовать синтаксис распространения в списке параметров, например:

function func(...args) {
  args.forEach(arg => console.log(arg))
}

const values = ['a', 'b', 'c']
func(...values)
func(1, 2, 3)

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

function func(first, second, ...theRest) {
  //...
}

И, возможно, вам будет полезно узнать, сколько аргументов ожидает функция:

var test = function (one, two, three) {}; 
test.length == 3;

Но в любом случае вы можете передать произвольное количество аргументов ...

Синтаксис распространения короче и «слаще», чем, applyи если вам не нужно устанавливать thisзначение в вызове функции, это путь.

Вот пример применения , который был первым способом сделать это:

var arr = ['a','b','c'];

function func() {
  console.log(this); // 'test'
  console.log(arguments.length); // 3

  for(var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }

};

func.apply('test', arr);

В настоящее время я рекомендую использовать applyтолько, если вам нужно передать произвольное количество аргументов из массива и установить thisзначение. applyпринимает thisзначение в качестве первых аргументов, которые будут использоваться при вызове функции; если мы используем nullв нестрогом коде, thisключевое слово будет ссылаться на глобальный объект (окно) внутри func, в строгом режиме, когда явно используется «использовать строгий». 'или в модулях ES, nullбудет использоваться.

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

var argsArray = Array.prototype.slice.call(arguments);

И в ES6:

const argsArray = [...arguments] // or Array.from(arguments)

Но вы редко используете argumentsобъект прямо сейчас, благодаря синтаксису распространения.

CMS
источник
1
Спасибо, применить делает трюк, вызов в этом случае не работает. это было написано по ошибке?
Fire Crow
Как вы это сделаете, если функция на самом деле является членом класса? Это правильно? theObject.member.apply (theObject, аргументы);
Baxissimo
4
Array.prototype.slice.call(arguments);предотвращает JS-оптимизацию браузера, такую ​​как V8. Подробности здесь
Дхирадж Бхаскар
2
1. Судя по всему, argumentsамортизация планируется в пользу оператора спреда . У меня нет источника для этого, но я где-то слышал. 2. MDN государства , что использование sliceна argumentsобъект предотвращает оптимизации в системах , как V8. Они предлагают использовать «презираемый конструктор массива» Array.from(arguments), или [...arguments]. Вы хотели бы обновить свой ответ для ES2015?
Брэден Бест
NB: я пошел дальше и написал свой собственный ответ .
Брэден Бест
75

На самом деле вы можете передать столько значений, сколько захотите, в любую функцию JavaScript. Явно названные параметры получат первые несколько значений, но ВСЕ параметры будут сохранены в массиве arguments.

Чтобы передать массив arguments в «распакованном» виде, вы можете использовать apply, например так (см. Функциональный Javascript ):

var otherFunc = function() {
   alert(arguments.length); // Outputs: 10
}

var myFunc = function() {
  alert(arguments.length); // Outputs: 10
  otherFunc.apply(this, arguments);
}
myFunc(1,2,3,4,5,6,7,8,9,10);
danben
источник
23

Операторы сплат и распространение являются частью ES6, планируемой следующей версии Javascript. Пока их поддерживает только Firefox. Этот код работает в FF16 +:

var arr = ['quick', 'brown', 'lazy'];

var sprintf = function(str, ...args)
{
    for (arg of args) {
        str = str.replace(/%s/, arg);
    }
    return str;
}

sprintf.apply(null, ['The %s %s fox jumps over the %s dog.', ...arr]);
sprintf('The %s %s fox jumps over the %s dog.', 'slow', 'red', 'sleeping');

Обратите внимание на синтаксис awkard для распространения. Обычный синтаксис sprintf('The %s %s fox jumps over the %s dog.', ...arr);пока не поддерживается . Вы можете найти таблицу совместимости ES6 здесь .

Обратите внимание также на использование for...ofеще одного дополнения ES6. Использование for...inдля массивов - плохая идея .

Tgr
источник
Нормальный синтаксис для распространения теперь поддерживается в Firefox и Chrome
пользователь
8

applyФункция принимает два аргумента; объект thisбудет связан с аргументами, представленными в виде массива.

some_func = function (a, b) { return b }
some_func.apply(obj, ["arguments", "are", "here"])
// "are"
Август Лиллеас
источник
8

Начиная с ES2015, существует два метода.

argumentsобъекта

Этот объект встроен в функции и, соответственно, ссылается на аргументы функции. Технически, однако, это не массив, поэтому типичные операции над массивами не будут работать с ним. Предлагаемый метод заключается в использовании Array.fromили оператора распространения для создания массива из него.

Я видел другие ответы, упоминающие использование slice. Не делай этого. Это предотвращает оптимизацию (источник: MDN ).

Array.from(arguments)
[...arguments]

Тем не менее, я бы сказал, что argumentsэто проблематично, поскольку скрывает то, что функция принимает в качестве входных данных. argumentsФункция , как правило , записывается следующим образом:

function mean(){
    let args = [...arguments];
    return args.reduce((a,b)=>a+b) / args.length;
}

Иногда заголовок функции пишется следующим образом для документирования аргументов в стиле C:

function mean(/* ... */){ ... }

Но это редко.

Что касается того, почему это проблематично, возьмем C, например. C обратно совместим с древним пре-ANSI диалектом языка, известного как K & R C. K & R C позволяет прототипам функций иметь пустой список аргументов.

int myFunction();
/* This function accepts unspecified parameters */

ANSI C предоставляет возможность для varargs( ...) и voidдля указания пустого списка аргументов.

int myFunction(void);
/* This function accepts no parameters */

Многие люди непреднамеренно объявляют функции с unspecifiedаргументами list ( int myfunction();), когда ожидают, что функция примет нулевые аргументы. Технически это ошибка, потому что функция будет принимать аргументы. Любое их количество.

Правильная varargsфункция в C принимает форму:

int myFunction(int nargs, ...);

И в JavaScript действительно есть нечто похожее на это.

Spread Operator / Rest Parameter

Я уже показал вам оператор спреда.

...name

Он довольно универсален и может также использоваться в списке аргументов функции («параметры покоя»), чтобы задавать переменные в хорошо документированном виде:

function mean(...args){
    return args.reduce((a,b)=>a+b) / args.length;
}

Или как лямбда-выражение:

((...args)=>args.reduce((a,b)=>a+b) / args.length)(1,2,3,4,5); // 3

Я предпочитаю спред оператора. Это чисто и самодокументировано.

Брэден Бест
источник
2
Спасибо за обновленный ответ, этот пост предшествует ES 2015, так что я рад, что вы
Fire Crow
5

Это называется splatоператором. Вы можете сделать это в JavaScript, используя apply:

var arr = ['a','b','c','d'];
var func = function() {
    // debug 
    console.log(arguments.length);
    console.log(arguments);
}
func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'
func.apply(null, arr); 
Чандра Патни
источник
4

С ES6 вы можете использовать остальные параметры для varagrs. Это берет список аргументов и преобразует его в массив.

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}
theres.yer.problem
источник
2

Это пример программы для вычисления суммы целых чисел для переменных аргументов и массива на целых числах. Надеюсь это поможет.

var CalculateSum = function(){
    calculateSumService.apply( null, arguments );
}

var calculateSumService = function(){
    var sum = 0;

    if( arguments.length === 1){
        var args = arguments[0];

        for(var i = 0;i<args.length; i++){
           sum += args[i]; 
        }
    }else{
        for(var i = 0;i<arguments.length; i++){
           sum += arguments[i]; 
        }        
    }

     alert(sum);

}


//Sample method call

// CalculateSum(10,20,30);         

// CalculateSum([10,20,30,40,50]);

// CalculateSum(10,20);
полоса
источник
1
Некоторые примечания: 1. varобъявляет function scopeпеременную. Новые letи constключевые слова (ES 2015) объявляют block scopeпеременные. До ES 2015, однако, переменные должны были все же быть выведены на вершину функции на практике. 2. Вашей функции пауза , когда данные 1 целочисленного значения из - за свое ошибочное принятие массива в качестве аргумента (вопрос спрашивает о переменной длине; не принимать массивы в качестве аргументов): CalculateSum(6) -> 0. (продолжение в следующем посте)
Брэден Бест
1
3. Не используйте функции отладки, такие как alert. На самом деле, просто не используйте alert/prompt/confirm, точка. Используйте console.logвместо этого при отладке. 4. Функция должна иметь returnзначение, а не alertего. 5. избегать, PascalCaseесли это не относится к «типу» данных. Т.е. класс. Используйте camelCaseили under_scoreвместо. 4. Мне не нравится, как вы объявляете свои функции. var f = function(){ ... }подразумевает, что вы хотите, чтобы это изменилось в какой-то момент, что, вероятно, не ваше намерение. function name(){ ... }Вместо этого используйте обычный синтаксис.
Брэден Бест
Чтобы продемонстрировать все эти критические замечания, вот улучшенная версия функции в вашем ответе.
Брэден Бест
2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);
yckart
источник
1

Для тех, кто был перенаправлен сюда с передачи переменного числа аргументов из одной функции в другую (что не должно быть помечено как дубликат этого вопроса):

Если вы пытаетесь передать переменное число аргументов из одной функции в другую, начиная с JavaScript 1.8.5, вы можете просто вызвать apply()вторую функцию и передать argumentsпараметр:

var caller = function()
{
    callee.apply( null, arguments );
}

var callee = function()
{
    alert( arguments.length );
}

caller( "Hello", "World!", 88 ); // Shows "3".

Примечание . Первый аргумент - это используемый thisпараметр. Передача nullвызовет функцию из глобального контекста, то есть как глобальную функцию вместо метода какого-либо объекта.

В соответствии с этим документом спецификация ECMAScript 5 переопределила apply()метод для принятия любого «универсального объекта, подобного массиву», вместо строго массива. Таким образом, вы можете напрямую передать argumentsсписок во вторую функцию.

Протестировано в Chrome 28.0, Safari 6.0.5 и IE 10. Попробуйте это с этим JSFiddle .

andrewtc
источник
0

Да, вы можете передать переменную нет. аргументов для функции. Вы можете использовать applyдля достижения этой цели.

Например:

var arr = ["Hi","How","are","you"];

function applyTest(){
    var arg = arguments.length;
    console.log(arg);
}

applyTest.apply(null,arr);
Abhijit
источник
0

Вы хотите, чтобы ваша функция реагировала на аргументы массива или переменные? Если последнее, попробуйте:

var func = function(...rest) {
  alert(rest.length);

  // In JS, don't use for..in with arrays
  // use for..of that consumes array's pre-defined iterator
  // or a more functional approach
  rest.forEach((v) => console.log(v));
};

Но если вы хотите обработать аргумент массива

var fn = function(arr) {
  alert(arr.length);

  for(var i of arr) {
    console.log(i);
  }
};
Дев Йего
источник