Чи можна надіслати змінну кількість аргументів функції 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, і це чудовий зразок вміти приймати вараги та надсилати їх. напр

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
це ніколи не було проблемою, чи можете ви докладно пояснити, чому це так? Об'єкт аргументів майже завжди є досить малим, щоб мати незначне поліпшення продуктивності використання версії agruments [0..length-1].
Вогнева ворона


1
Я думаю, що аргументи - це не власне масив, а спеціальний об'єкт, тому синтаксис "in" може не працювати, залежно від реалізації JS.
O'Rooney

Відповіді:


208

Оновлення : з 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об'єкт прямо сьогодні завдяки синтаксису розповсюдження.


1
Дякуємо, застосувати робить фокус, дзвінок у цьому випадку не працює. це було написано помилково?
Вогнева ворона

Як це зробити, якщо функція насправді є членом класу? Чи це правильно? theObject.member.apply (theObject, аргументи);
Baxissimo

4
Array.prototype.slice.call(arguments);запобігає оптимізації веб-переглядача на зразок V8. Деталі тут
Dheeraj Bhaskar

2
1. Мабуть, argumentsпланується знехтувати на користь оператора розповсюдження . У мене немає джерела для цього, але я його десь чув. 2. MDN держави , що використання sliceна argumentsоб'єкт запобігає оптимізації в системах , як V8. Вони пропонують використовувати "зневажаний конструктор масиву" Array.from(arguments), або [...arguments]. Бажаєте оновити свою відповідь на ES2015?
Бреден Кращий

NB: Я пішов вперед і написав власну відповідь .
Бреден Кращий

75

Насправді ви можете передавати стільки значень, скільки хочете, до будь-якої функції javascript. Явно названі параметри отримають перші кілька значень, але ВСІ параметри будуть збережені в масиві аргументів.

Щоб передати масив аргументів у "розпакованій" формі, ви можете застосувати, наприклад, так (cf Functional 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);

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');

Зверніть увагу на синтаксис авангарду для поширення. Звичайний синтаксисsprintf('The %s %s fox jumps over the %s dog.', ...arr); ще не підтримується . Таблицю сумісності ES6 можна знайти тут .

Зауважте також використання for...ofще одного додатка ES6. Використання for...inдля масивів - погана ідея .


Звичайний синтаксис для поширення тепер підтримується в 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 назад сумісний із стародавнім діалектом до 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списком аргументів ( int myfunction();), коли очікують, що функція прийме нульові аргументи. Технічно це помилка, оскільки функція буде приймати аргументи. Будь-яка їх кількість.

Власна varargsфункція в C має вигляд:

int myFunction(int nargs, ...);

І JavaScript насправді має щось подібне до цього.

Розподіліть параметри оператора / відпочинку

Насправді я вам уже показав оператор розповсюдження.

...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)
    }
}

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);

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 .


0

Так, ви можете передати змінну ні. аргументів функції. Ви можете використовувати applyдля досягнення цього.

Наприклад:

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

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

applyTest.apply(null,arr);

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);
  }
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.