Змінна кількість аргументів для функціонування


531

Чи є спосіб дозволити "необмежену" vars для функції в JavaScript?

Приклад:

load(var1, var2, var3, var4, var5, etc...)
load(var1)


пов'язаний / можливий дублікат stackoverflow.com/questions/4633125/…
Лук

1
@Luke ні, це не так. Це питання задає питання, як викликати функцію з довільною кількістю аргументів з аргументами в масиві. Це запитує, як поводитися з таким дзвінком.
Scruffy

1
Для легшого пошуку таку функцію називають "варіатичною функцією".
gschenk

Відповіді:


814

Звичайно, просто використовуйте argumentsоб'єкт.

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

1
Tnx. Він відмінно підходить для розбору рядків з рідного коду Android на javascript у веб-перегляді.
Йоган Хоексма

4
Це рішення найкраще працювало для мене. Дякую. Детальна інформація про ключове слово аргументи ТУТ .
Користувач2

26
argumentsце спеціальний "схожий на масив" об'єкт, що означає, що він має довжину, але інших функцій масиву немає. Див developer.mozilla.org/en-US/docs/Web/JavaScript/Reference / ... для отримання додаткової інформації, і ця відповідь: stackoverflow.com/a/13145228/1766230
Люк

4
Цікаво, що методи масиву в Javascript були визначені таким чином, що вони працюють на будь-якому об'єкті, який має lengthвластивість. Сюди входить argumentsоб’єкт. Знаючи це, і що метод concatповертає копію з «масиву» це називається, ми можемо легко перетворити argumentsоб'єкт в реальний масив , як це: var args = [].concat.call(arguments). Деякі люди вважають за краще використовувати Array.prototype.concat.callзамість цього, але мені подобається [], вони короткі і солодкі!
Штійн де Вітт

2
@YasirJan використання[...arguments].join()
Вілліан Д. Андраде

179

У (більшості) останніх веб-переглядачах ви можете приймати змінну кількість аргументів із цим синтаксисом:

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

Ось невеликий приклад:

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
    0: "a"
    1: "b"
    2: "c"
    3: "d"
    length: 4

Документація та інші приклади тут: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters


27
FYI його називають "синтаксисом параметра відпочинку": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tanguy_k

4
+1 Це елегантне та чисте рішення. Особливо підходить для переходу через довгий список параметрів в інший виклик функції та, можливо, ці змінні параметри знаходяться у довільному положенні.
haxpor

3
Майте на увазі, що згідно з developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… він не підтримується в IE.
Roee Gavirel

2
Ось таблиця із підтримкою браузера - caniuse.com/#feat=rest-parameters
Брайан Бернс

1
Така приємна відповідь!
Ашиш

113

Інший варіант - передати свої аргументи в контекстний об'єкт.

function load(context)
{
    // do whatever with context.name, context.address, etc
}

і використовувати його так

load({name:'Ken',address:'secret',unused:true})

Це має перевагу в тому, що ви можете додати стільки іменованих аргументів, скільки хочете, і функція може використовувати їх (чи ні), як вважаєте за потрібне.


4
Це було б краще, оскільки він видаляє зв'язок до порядку аргументів. Слабо поєднані інтерфейси - це добра стандартна практика ...
Йонас Шуберт Ерландссон

9
Звичайно, це краще в деяких випадках. Скажімо, окремі аргументи насправді не стосуються один одного, або всі вони повинні мати однакове значення (як елементи масиву). Тоді спосіб ОП найкращий.
rvighne

1
Це також приємно, тому що якщо ви хочете, ви можете побудувати contextаргумент з кодом і передати його навколо, перш ніж він звикне .
Nate CK

46

Я погоджуюся з відповіддю Кена як найбільш динамічного, і мені подобається зробити його на крок далі. Якщо це функція, яку ви викликаєте кілька разів з різними аргументами - я використовую дизайн Ken, але потім додаю значення за замовчуванням:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

Таким чином, якщо у вас є багато параметрів, але не обов'язково їх встановлювати з кожним викликом функції, ви можете просто вказати не за замовчуванням. Для методу розширення ви можете використовувати метод розширення jQuery ( $.extend()), створити свій власний або скористатися наступним:

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

Це об'єднає контекстний об'єкт із замовчуванням і заповнить будь-які невизначені значення у вашому об'єкті за замовчуванням.


3
+1. Гарний трюк. Економить багато плит котла, щоб кожен параметр був визначений, за замовчуванням чи іншим чином.
Ніл

1
_.defaults()Метод підкреслення - це дуже приємна альтернатива об'єднанню заданих та типових аргументів.
mbeasley


18

Переважно використовувати синтаксис параметра відпочинку, як вказав Рамаст.

function (a, b, ...args) {}

Я просто хочу додати трохи приємного властивості аргументу ... args

  1. Це масив, а не об'єкт, як аргументи. Це дозволяє безпосередньо застосовувати такі функції, як карта чи сортування.
  2. Він не включає всі параметри, а лише той, який передається від нього далі. Напр. Функція (a, b, ... args) у цьому випадку args містить аргумент 3 до argument.length

15

Як вже було сказано, ви можете використовувати arguments об'єкт для отримання змінної кількості параметрів функції.

Якщо ви хочете викликати іншу функцію з тими ж аргументами, використовуйте apply. Ви навіть можете додавати або видаляти аргументи, перетворюючи argumentsна масив. Наприклад, ця функція вставляє текст перед входом у консоль:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}

приємне рішення для перетворення аргументів в масив. Це було мені сьогодні корисно.
Дмитро Медвідь

8

Хоча я загалом погоджуюся, що підхід із названими аргументами є корисним та гнучким (якщо ви не піклуєтесь про порядок, і в цьому випадку аргументи найпростіші), у мене є сумніви щодо вартості підходу mbeasley (з використанням за замовчуванням та розширеннями). Це велика вартість, яку потрібно взяти для витягування значень за замовчуванням. По-перше, параметри за замовчуванням визначаються всередині функції, тому вони перенаселяються під час кожного дзвінка. По-друге, ви можете легко прочитати названі значення та одночасно встановити параметри за замовчуванням, використовуючи || Для отримання цієї інформації не потрібно створювати й об’єднувати ще один новий об’єкт.

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

Це приблизно однакова кількість коду (може бути трохи більше), але має бути часткою витрат на виконання.


Погоджено, хоча шкода залежить від типу значення або самого дефолту. В іншому випадку (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)або для меншої гучності коду невелика помічна функція, наприклад: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)надайте альтернативні схеми. Однак моя думка все ще стоїть: створення додаткових об'єктів для кожного виклику функції та запуск дорогих циклів для встановлення пари значень за замовчуванням - шалена сума накладних витрат.
mcurland

7

Хоча @roufamatic показав використання ключового слова аргументів, а @Ken показав чудовий приклад об’єкта для використання, я не відчуваю, що по-справжньому звертався до того, що відбувається в даному випадку, і може заплутати майбутніх читачів або прищепити погану практику, оскільки явно не вказує функцію. / метод призначений приймати змінну кількість аргументів / параметрів.

function varyArg () {
    return arguments[0] + arguments[1];
}

Коли інший розробник переглядає ваш код, дуже легко припустити, що ця функція не приймає параметрів. Особливо, якщо той розробник не прихильний до аргументів ключового слова . Через це є хорошою ідеєю слідувати настановам стилю та бути послідовними. Я буду використовувати Google для всіх прикладів.

Давайте явно констатуємо, що однакова функція має змінні параметри:

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

Параметр об'єкта VS var_args

Можуть бути випадки, коли потрібен об’єкт, оскільки це єдиний затверджений і вважається найкращим методом практики використання даних. Асоціативні масиви нахмурені та відсторонені.

SIDENOTE: Ключове слово аргументи фактично повертає назад об’єкт, використовуючи числа як ключ. Прототиповим успадкуванням є також сімейство об'єктів. Дивіться кінець відповіді щодо правильного використання масиву в JS

У цьому випадку ми можемо також прямо сказати про це. Примітка: ця умова про іменування не надається Google, але є прикладом явного оголошення типу парамнезу. Це важливо, якщо ви хочете створити в своєму коді більш суворий введений зразок.

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

Який вибрати?

Це залежить від потреб вашої функції та програми. Якщо, наприклад, ви просто хочете повернути базу значень в ітераційному процесі для всіх переданих аргументів, то, безумовно, дотримуйтесь ключового слова аргументів . Якщо вам потрібне визначення ваших аргументів та відображення даних, тоді об'єктним методом є шлях. Давайте розглянемо два приклади, і тоді ми закінчимо!

Використання аргументів

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

Використання об'єкта

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

Доступ до масиву замість об'єкта ("... args" Параметр решта)

Як було сказано вгорі відповіді, ключове слово аргументи фактично повертає об'єкт. Через це потрібно буде викликати будь-який метод, який ви хочете використовувати для масиву. Приклад цього:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

Щоб уникнути цього, використовуйте параметр "решта":

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5

5

Використовуйте argumentsоб'єкт, коли всередині функції, щоб мати доступ до всіх переданих аргументів.


3

Майте на увазі, що передача Об'єкта з названими властивостями, як запропонував Кен, додає вартість виділення та звільнення тимчасового об'єкта для кожного виклику. Передача звичайних аргументів за значенням або посиланням, як правило, є найбільш ефективною. Для багатьох застосувань, хоча продуктивність не є критичною, але для деяких вона може бути.

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