Varios argumentos vs objeto de opciones


Al crear una función JavaScript con múltiples argumentos, siempre me enfrento a esta opción: pasar una lista de argumentos vs.pasar un objeto options.

Por ejemplo, estoy escribiendo una función para asignar una lista de nodos a una matriz:

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

Podría usar esto:

function map(options){
    ...
}

Donde options es un objeto:

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

¿Cuál es la forma recomendada? Existen directrices para cuándo utilizar uno contra el otro?

[Actualización] Parece haber un consenso a favor del objeto options, así que me gustaría añadir un comentario: una de las razones por las que estaba tentado a utilizar la lista de argumentos en mi caso fue tener un comportamiento consistente con el JavaScript construido en la matriz.método map.

Author: Christophe, 2012-10-10

10 answers

Como muchos de los otros, a menudo prefiero pasar un options object a una función en lugar de pasar una larga lista de parámetros, pero realmente depende del contexto exacto.

Utilizo la legibilidad del código como prueba de fuego.

Por ejemplo, si tengo esta llamada a la función:

checkStringLength(inputStr, 10);

Creo que el código es bastante legible de la forma en que es y pasar parámetros individuales está bien.

Por otro lado, hay funciones con llamadas como esta:

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

Completamente ilegible a menos que investigues un poco. Por otro lado, este código se lee bien:

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

Es más un arte que una ciencia, pero si tuviera que nombrar reglas de oro:

Utilice un parámetro options si:

  • Tienes más de cuatro parámetros
  • Cualquiera de los parámetros es opcional
  • Alguna vez has tenido que buscar la función para averiguar qué parámetros toma
  • Si alguien alguna vez intenta estrangularte mientras grita " ARRRRRG!"
 108
Author: Jeremy J Starcher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-14 10:51:38

Los argumentos múltiples son principalmente para parámetros obligatorios. No les pasa nada.

Si tiene parámetros opcionales, se complica. Si uno de ellos se basa en los otros, de modo que tienen un cierto orden (por ejemplo, el cuarto necesita el tercero), aún debe usar varios argumentos. Casi todos los métodos nativos de ECMAScript y DOM funcionan así. Un buen ejemplo es el open método de XMLHttpRequests, donde los últimos 3 argumentos son opcionales - la regla es como "no hay contraseña sin un usuario" (ver también MDN docs).

Los objetos de opción son útiles en dos casos:

  • Tienes tantos parámetros que se vuelve confuso: El" naming " te ayudará, no tienes que preocuparte por el orden de ellos (especialmente si pueden cambiar)
  • Tienes parámetros opcionales. Los objetos son muy flexibles, y sin ningún tipo de orden que acaba de pasar las cosas que necesita y nada más (o undefineds).

En su caso, yo recomendaría map(nodeList, callback, options). nodelist y callback son requeridos, los otros tres argumentos vienen solo ocasionalmente y tienen valores predeterminados razonables.

Otro ejemplo es JSON.stringify. Es posible que desee utilizar el parámetro space sin pasar una función replacer - entonces usted tiene que llamar …, null, 4). Un objeto arguments podría haber sido mejor, aunque no es realmente razonable para solo 2 parámetros.

 22
Author: Bergi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-11 10:55:25

Usar el enfoque 'opciones como objeto' va a ser mejor. No tiene que preocuparse por el orden de las propiedades y hay más flexibilidad en qué datos se pasan (parámetros opcionales, por ejemplo)

Crear un objeto también significa que las opciones podrían usarse fácilmente en múltiples funciones:

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

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

function2(options){
    alert(options.fromIndex);
}
 11
Author: Fluidbyte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-10 19:47:33

Creo que si estás instanciando algo o llamando a un método de un objeto, quieres usar un objeto options. Si se trata de una función que opera en solo uno o dos parámetros y devuelve un valor, es preferible una lista de argumentos.

En algunos casos, es bueno usar ambos. Si su función tiene uno o dos parámetros requeridos y un montón de parámetros opcionales, haga que los dos primeros parámetros requeridos y el tercero un hash de opciones opcional.

En tu ejemplo, haría map(nodeList, callback, options). Nodelist y se requieren devoluciones de llamada, es bastante fácil saber lo que está sucediendo con solo leer una llamada, y es como las funciones de mapa existentes. Cualquier otra opción se puede pasar como un tercer parámetro opcional.

 7
Author: Trevor Dixon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-10 19:55:23

Su comentario sobre la pregunta:

En mi ejemplo los tres últimos son opcionales.

Entonces, ¿por qué no hacer esto? (Nota: Esto es Javascript bastante crudo. Normalmente usaría un hash default y lo actualizaría con las opciones pasadas usando el objeto .extender o jQuery.extender o similar..)

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

Así que, ahora que es mucho más obvio lo que es opcional y lo que no, todos estos son usos válidos de la función:

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,
});
 5
Author: Izkata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-11 02:47:16

Depende.

Basado en mi observación sobre el diseño de esas bibliotecas populares, aquí están los escenarios que debemos usar option object:

  • La lista de parámetros es larga (>4).
  • Algunos o todos los parámetros son opcionales y no dependen de un cierto orden.
  • La lista de parámetros podría crecer en futuras actualizaciones de la API.
  • La API se llamará desde otro código y el nombre de la API no está claro suficiente para decir el significado de los parámetros. Por lo que podría necesitar fuerte nombre del parámetro para la legibilidad.

Y escenarios para usar la lista de parámetros:

  • La lista de parámetros es corta (
  • La mayoría o todos los parámetros son requeridos.
  • Los parámetros opcionales están en cierto orden. (es decir: $.get)
  • Fácil de decir los parámetros que significan por nombre de API.
 3
Author: Lynn Ning,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-15 19:37:10

Object es más preferible, porque si pasa un objeto es fácil extender el número de propiedades en esos objetos y no tiene que vigilar el orden en el que se han pasado sus argumentos.

 2
Author: happyCoda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-10 19:37:10

Para una función que normalmente usa algunos argumentos predefinidos, es mejor usar option object. El ejemplo opuesto será algo así como una función que obtiene un número infinito de argumentos como: setCSS({height:100},{width:200},{background:"#000"}).

 1
Author: Ilya Sidorovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-10 19:56:10

Puede que llegue un poco tarde a la fiesta con esta respuesta, pero estaba buscando las opiniones de otros desarrolladores sobre este tema y me encontré con este hilo.

Estoy muy en desacuerdo con la mayoría de los respondedores, y estoy de acuerdo con el enfoque de "argumentos múltiples". Mi argumento principal es que desalienta otros anti-patrones como "mutar y devolver el objeto param", o "pasar el mismo objeto param a otras funciones". He trabajado en bases de código que han abusado ampliamente este anti-patrón, y el código de depuración que hace esto rápidamente se vuelve imposible. Creo que esta es una regla general muy específica de Javascript, ya que Javascript no está fuertemente escrito y permite tales objetos arbitrariamente estructurados.

Mi opinión personal es que los desarrolladores deben ser explícitos al llamar a funciones, evitar pasar datos redundantes y evitar modificar por referencia. No es que estos patrones impidan escribir código conciso y correcto. Siento que lo hace mucho más fácil para que su proyecto caiga en malas prácticas de desarrollo.

Considere el siguiente código terrible:

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

Este tipo de documentación no solo requiere más explícita para especificar la intención, sino que también deja espacio para errores vagos. ¿Qué pasa si un desarrollador modifica param1 en bar()? ¿Cuánto tiempo crees que tomaría mirar a través de una base de código de tamaño suficiente para atrapar esto? Es cierto que este ejemplo es ligeramente falso porque asume que los desarrolladores ya se han comprometido varios anti-patrones en este punto. Pero muestra cómo pasar objetos que contienen parámetros permite un mayor espacio para el error y la ambigüedad, lo que requiere un mayor grado de conciencia y observancia de la corrección de la constancia.

¡Solo mis dos centavos en el tema!

 1
Author: ajxs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 00:29:44

Me gustaría mirar grandes proyectos de javascript.

Cosas como google map verá con frecuencia que los objetos instanciados requieren un objeto, pero las funciones requieren parámetros. Creo que esto tiene que ver con ARGUMENTOS DE OPCIÓN.

Si necesita argumentos predeterminados o argumentos opcionales, un objeto probablemente sería mejor porque es más flexible. Pero si no lo haces argumentos funcionales normales son más explícitos.

Javascript tiene un objeto arguments demasiado. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

 0
Author: dm03514,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-10 19:42:23