Множество объектов аргументов и опций

При создании функции JavaScript с несколькими аргументами я всегда сталкиваюсь с этим выбором: передайте список аргументов и передайте объект опций.

Например, я пишу функцию для сопоставления nodeList с массивом:

function map(nodeList, callback, thisObject, fromIndex, toIndex){
    ...
}

Вместо этого я мог бы использовать это:

function map(options){
    ...
}

где options - это объект:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

Какой из них рекомендуется? Существуют ли рекомендации по использованию одного и другого?

[Обновление] Кажется, существует консенсус в пользу объекта options, поэтому я хотел бы добавить комментарий: одна из причин, почему я был склонен использовать список аргументов в моем случае, заключался в том, чтобы иметь согласованное поведение с JavaScript, встроенным в метод array.map.

Ответ 1

Как и многие другие, я часто предпочитаю передавать options object функции вместо передачи длинного списка параметров, но это действительно зависит от точного контекста.

Я использую читаемость кода как тест лакмусовой бумажки.

Например, если у меня есть вызов этой функции:

checkStringLength(inputStr, 10);

Я думаю, что код достаточно читабельный, как есть, и передавать отдельные параметры просто отлично.

С другой стороны, существуют функции с такими вызовами:

initiateTransferProtocol("http", false, 150, 90, null, true, 18);

Полностью нечитабельно, если вы не выполните некоторые исследования. С другой стороны, этот код хорошо читается:

initiateTransferProtocol({
  "protocol": "http",
  "sync":      false,
  "delayBetweenRetries": 150,
  "randomVarianceBetweenRetries": 90,
  "retryCallback": null,
  "log": true,
  "maxRetries": 18
 });

Это скорее искусство, чем наука, но если бы мне пришлось называть эмпирические правила:

Используйте параметр options, если:

  • У вас есть более четырех параметров
  • Любой из параметров является необязательным
  • Вам приходилось искать функцию, чтобы выяснить, какие параметры она принимает.
  • Если кто-то пытается задушить вас, крича "ARRRRRG!"

Ответ 2

Несколько аргументов в основном относятся к обязательным параметрам. Там нет ничего плохого.

Если у вас есть дополнительные параметры, он становится сложным. Если один из них полагается на других, так что у них есть определенный порядок (например, четвертый нуждается в третьем), вы все равно должны использовать несколько аргументов. Практически все родные EcmaScript и DOM-методы работают так. Хорошим примером является метод open XMLHTTPrequests, где последние 3 аргумента являются необязательными - правило похоже на "без пароля без пользователя" (см. Также MDN docs).

Опционные объекты пригождаются в двух случаях:

  • У вас так много параметров, что это запутывает: "Именование" вам поможет, вам не нужно беспокоиться о порядке их (особенно, если они могут измениться)
  • У вас есть дополнительные параметры. Объекты очень гибкие, и без какого-либо заказа вы просто передаете нужные вам вещи и ничего больше (или undefined s).

В вашем случае я бы рекомендовал map(nodeList, callback, options). nodelist и callback требуются, остальные три аргумента появляются только изредка и имеют разумные значения по умолчанию.

Другим примером является JSON.stringify. Возможно, вы захотите использовать параметр space без передачи функции replacer - тогда вам нужно позвонить …, null, 4). Объект arguments мог бы быть лучше, хотя его не очень разумно только для двух параметров.

Ответ 3

Использование подхода "параметры как объект" будет лучше. Вам не нужно беспокоиться о порядке свойств и гибкости в том, какие данные передаются (например, необязательные параметры)

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

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

function1(options){
    alert(options.nodeList);
}

function2(options){
    alert(options.fromIndex);
}

Ответ 4

Я думаю, что если вы что-то создаете или называете метод объекта, вы хотите использовать объект options. Если это функция, которая работает только с одним или двумя параметрами и возвращает значение, предпочтительнее список аргументов.

В некоторых случаях полезно использовать оба. Если у вашей функции есть один или два требуемых параметра и куча необязательных, сделайте первые два параметра, а третий - опциональным хешем.

В вашем примере я бы сделал map(nodeList, callback, options). Требуется нумерация и обратный вызов, довольно легко сказать, что происходит, просто прочитав вызов, и это похоже на существующие функции карты. Любые другие параметры могут быть переданы как дополнительный третий параметр.

Ответ 5

Ваш комментарий к вопросу:

в моем примере последние три являются необязательными.

Так почему бы не сделать это? (Примечание: это довольно грубый Javascript. Обычно я использую хэш default и обновляю его с помощью опций, переданных с помощью Object.extend или JQuery.extend или тому подобное..)

function map(nodeList, callback, options) {
   options = options || {};
   var thisObject = options.thisObject || {};
   var fromIndex = options.fromIndex || 0;
   var toIndex = options.toIndex || 0;
}

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

map(nodeList, callback);
map(nodeList, callback, {});
map(nodeList, callback, null);
map(nodeList, callback, {
   thisObject: {some: 'object'},
});
map(nodeList, callback, {
   toIndex: 100,
});
map(nodeList, callback, {
   thisObject: {some: 'object'},
   fromIndex: 0,
   toIndex: 100,
});

Ответ 6

Это зависит.

Основываясь на моем наблюдении за дизайном этих популярных библиотек, вот сценарии, которые мы должны использовать объект option:

  • Список параметров длинный ( > 4).
  • Некоторые или все параметры являются необязательными, и они не полагаются на определенный заказ.
  • Список параметров может появиться в будущем обновлении API.
  • API будет вызываться из другого кода, а имя API не понятно достаточно сказать значения параметров. Поэтому может потребоваться имя параметра для удобочитаемости.

И сценарии использования списка параметров:

  • Список параметров короткий (< = 4).
  • Требуется большинство или всех параметров.
  • Дополнительные параметры находятся в определенном порядке. (т.е.: $.get)
  • Легко рассказать о значении параметров по имени API.

Ответ 7

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

Ответ 8

Для функции, которая обычно использует некоторые предопределенные аргументы, вам лучше использовать объект option. Обратный пример будет чем-то вроде функции, которая получает бесконечное количество аргументов, таких как: setCSS ({height: 100}, {width: 200}, {background: "# 000" }).

Ответ 9

Я бы посмотрел на большие проекты javascript.

Такие вещи, как google map, вы часто увидите, что объекты-объекты, требующие объекта, требуют объекта, но функции требуют параметров. Я бы подумал, что это связано с аргументами OPTION.

Если вам нужны аргументы по умолчанию или необязательные аргументы, объект, вероятно, будет лучше, потому что он более гибкий. Но если вы не используете обычные функциональные аргументы, более явные.

Javascript также имеет объект arguments. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

Ответ 10

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

Я очень не согласен с большинством респондентов и сторонником подхода "множественных аргументов". Мой главный аргумент заключается в том, что он не поощряет другие анти-шаблоны, такие как "мутация и возвращение объекта param" или "передача одного и того же объекта param на другие функции". Я работал в кодовых базах, которые сильно злоупотребляли этим анти-шаблоном, и код отладки, который делает это быстро, становится невозможным. Я считаю, что это очень специфичное для Javascript правило, поскольку Javascript не строго типизирован и допускает такие произвольно структурированные объекты.

Мое личное мнение заключается в том, что разработчики должны быть явными при вызове функций, избегать передачи избыточных данных и избегать модификации по ссылке. Это не то, что эти шаблоны не позволяют писать краткий, правильный код. Я просто чувствую, что для вашего проекта намного легче впасть в плохие методы разработки.

Рассмотрим следующий ужасный код:

function main() {
    const x = foo({
        param1: "something",
        param2: "something else",
        param3: "more variables"
    });

    return x;
}

function foo(params) {
    params.param1 = "Something new";
    bar(params);
    return params;
}


function bar(params) {
    params.param2 = "Something else entirely";
    const y = baz(params);
    return params.param2;
}

function baz(params) {
    params.params3 = "Changed my mind";
    return params;
}

Это не только требует более четкой документации, чтобы указать намерение, но также оставляет место для неопределенных ошибок. Что делать, если разработчик изменяет param1 в bar()? Как долго, по-твоему, понадобится проглядеть кодовую базу достаточного размера, чтобы поймать это? По общему признанию, этот пример немного неискренен, поскольку предполагает, что разработчики уже совершили несколько анти-шаблонов к этому моменту. Но это показывает, как передающие объекты, содержащие параметры, позволяют больше пространства для ошибки и двусмысленности, требуя большей степени добросовестности и соблюдения корректности const.

Только мои двухценты по этому вопросу!