Parámetros con nombre en javascript


Encuentro la característica de parámetros nombrados en C# bastante útil en algunos casos.

calculateBMI(70, height: 175);

¿Qué pasa si quiero esto en javascript?


Lo que no quiero es -

myFunction({ param1 : 70, param2 : 175});

function myFunction(params){
    //check if params is an object
    //check if the parameters I need are non-null
    //blah-blah
}

Ese enfoque ya lo he usado. Hay otra manera?

Estoy bien usando cualquier biblioteca hacer esto. (O alguien me puede señalar a uno que ya lo hace)

Author: Bergi, 2012-08-03

7 answers

ES2015

En ES2015, la desestructuración de parámetros se puede usar para simular parámetros con nombre. Requeriría que la persona que llama pase un objeto, pero puede evitar todas las comprobaciones dentro de la función si también usa parámetros predeterminados:

myFunction({ param1 : 70, param2 : 175});

function myFunction({param1, param2}={}){
  // ...
}

ES5

Hay una manera de acercarse a lo que quiere, pero se basa en la salida de Function.prototype.toString [ES5], que es depende de la implementación de algunos grado, por lo que podría no ser compatible con varios navegadores.

La idea es analizar los nombres de los parámetros de la representación de cadena de la función para que pueda asociar las propiedades de un objeto con el parámetro correspondiente.

Una llamada a una función podría entonces parecerse a

func(a, b, {someArg: ..., someOtherArg: ...});

Donde a y b son argumentos posicionales y el último argumento es un objeto con argumentos nombrados.

Por ejemplo:

var parameterfy = (function() {
    var pattern = /function[^(]*\(([^)]*)\)/;

    return function(func) {
        // fails horribly for parameterless functions ;)
        var args = func.toString().match(pattern)[1].split(/,\s*/);

        return function() {
            var named_params = arguments[arguments.length - 1];
            if (typeof named_params === 'object') {
                var params = [].slice.call(arguments, 0, -1);
                if (params.length < args.length) {
                    for (var i = params.length, l = args.length; i < l; i++) {
                        params.push(named_params[args[i]]);
                    }
                    return func.apply(this, params);
                }
            }
            return func.apply(null, arguments);
        };
    };
}());

Que usarías as:

var foo = parameterfy(function(a, b, c) {
    console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);     
});

foo(1, 2, 3); // a is 1  | b is 2  | c is 3
foo(1, {b:2, c:3}); // a is 1  | b is 2  | c is 3
foo(1, {c:3}); // a is 1  | b is undefined  | c is 3
foo({a: 1, c:3}); // a is 1  | b is undefined  | c is 3 

DEMO

Hay algunos inconvenientes a este enfoque (usted ha sido advertido!):

  • Si el último argumento es un objeto, se trata como un "objeto de argumento con nombre"
  • Siempre obtendrá tantos argumentos como haya definido en la función, pero algunos de ellos podrían tener el valor undefined (eso es diferente de no tener ningún valor). Eso significa que no puede usar arguments.length para probar cuántos argumentos se han pasar.

En lugar de tener una función que cree el wrapper, también podría tener una función que acepte una función y varios valores como argumentos, como

call(func, a, b, {posArg: ... });

O incluso extender Function.prototype para que puedas hacer:

foo.execute(a, b, {posArg: ...});
 113
Author: Felix Kling,
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
2016-01-26 18:14:27

No - el enfoque de objetos es la respuesta de JavaScript a esto. No hay problema con esto siempre que su función espere un objeto en lugar de parámetros separados.

 55
Author: Utkanos,
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-09-02 05:59:23

Este tema ha sido una molestia para mí durante algún tiempo. Soy un programador experimentado con muchos idiomas en mi haber. Uno de mis lenguajes favoritos que he tenido el placer de usar es Python. Python soporta parámetros con nombre sin ningún truco.... Desde que empecé a usar Python (hace algún tiempo) todo se hizo más fácil. Creo que todos los idiomas deben admitir parámetros con nombre, pero ese no es el caso.

Mucha gente dice que simplemente use el truco de "Pasar un objeto" para que ha nombrado parámetros.

/**
 * My Function
 *
 * @param {Object} arg1 Named arguments
 */
function myFunc(arg1) { }

myFunc({ param1 : 70, param2 : 175});

Y eso funciona muy bien, excepto..... cuando se trata de la mayoría de los IDE, muchos de nosotros los desarrolladores confiamos en sugerencias de tipo / argumento dentro de nuestro IDE. Yo personalmente uso PHP Storm (Junto con otros IDEs de JetBrains como PyCharm para python y AppCode para Objective C)

Y el mayor problema con el uso del truco "Pasar un objeto" es que cuando está llamando a la función, el IDE le da una pista de tipo único y eso es todo... Cómo se supone que vamos a ¿sabe qué parámetros y tipos deben entrar en el objeto arg1?

No tengo idea de qué parámetros deben ir en arg1

So... el truco de "Pasar un objeto" no funciona para mí... En realidad causa más dolores de cabeza con tener que mirar docblock de cada función antes de saber qué parámetros espera la función.... Claro, es genial para cuando se está manteniendo el código existente, pero es horrible para escribir código nuevo.

Bueno, esta es la técnica que uso.... Ahora, puede haber algunos problemas con él, y algunos desarrolladores pueden dime que lo estoy haciendo mal, y tengo una mente abierta cuando se trata de estas cosas... Siempre estoy dispuesto a buscar mejores formas de lograr una tarea... Por lo tanto, si hay un problema con esta técnica, entonces los comentarios son bienvenidos.

/**
 * My Function
 *
 * @param {string} arg1 Argument 1
 * @param {string} arg2 Argument 2
 */
function myFunc(arg1, arg2) { }

var arg1, arg2;
myFunc(arg1='Param1', arg2='Param2');

De esta manera, tengo lo mejor de ambos mundos... el nuevo código es fácil de escribir ya que mi IDE me da todas las pistas de argumento adecuadas... Y, manteniendo el código más adelante, puedo ver de un vistazo, no solo el valor pasado a la función, sino también el nombre del argumento. La única sobrecarga que veo es declarar los nombres de sus argumentos como variables locales para evitar contaminar el espacio de nombres global. Claro, es un poco de escritura extra, pero trivial en comparación con el tiempo que lleva buscar docblocks mientras se escribe código nuevo o se mantiene el código existente.

Ahora, tengo todos los parámetros y tipos al crear código nuevo

 17
Author: Ray Perea,
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
2014-07-10 20:06:24

Si desea dejar claro cuáles son cada uno de los parámetros, en lugar de simplemente llamar a

someFunction(70, 115);

¿Por qué no hacer lo siguiente

var width = 70, height = 115;
someFunction(width, height);

Claro, es una línea de código extra, pero gana en legibilidad.

 15
Author: dav_i,
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
2014-06-03 13:29:59

Otra forma sería usar atributos de un objeto adecuado, por ejemplo:

function plus(a,b) { return a+b; };

Plus = { a: function(x) { return { b: function(y) { return plus(x,y) }}}, 
         b: function(y) { return { a: function(x) { return plus(x,y) }}}};

sum = Plus.a(3).b(5);

Por supuesto, para este ejemplo inventado no tiene sentido. Pero en los casos en que la función se ve como

do_something(some_connection_handle, some_context_parameter, some_value)

Podría ser más útil. También podría combinarse con la idea" parameterfy " para crear tal objeto a partir de una función existente de una manera genérica. Es decir, para cada parámetro se crearía un miembro que puede evaluar a una versión parcial evaluada de la función.

Esta idea está, por supuesto, relacionada con Schönfinkeling también conocido como Currying.

 4
Author: Udo Klein,
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-02-07 15:40:38

Hay otro camino. Si pasa un objeto por referencia, las propiedades de ese objeto aparecerán en el ámbito local de la función. Sé que esto funciona para Safari (no he comprobado otros navegadores) y no se si esta característica tiene un nombre, pero el siguiente ejemplo ilustra su uso.

Aunque en la práctica no creo que esto ofrezca ningún valor funcional más allá de la técnica que ya está utilizando, es un poco más limpio semánticamente. Y todavía requiere pasar un objeto referencia o un objeto literal.

function sum({ a:a, b:b}) {
    console.log(a+'+'+b);
    if(a==undefined) a=0;
    if(b==undefined) b=0;
    return (a+b);
}

// will work (returns 9 and 3 respectively)
console.log(sum({a:4,b:5}));
console.log(sum({a:3}));

// will not work (returns 0)
console.log(sum(4,5));
console.log(sum(4));
 3
Author: Allen Ellison,
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
2015-05-02 23:43:58

Probando Node-6.4.0 ( process.versiones.v8 = '5.0.71.60') y el nodo Chakracore-v7.0.0-pre8 y luego Chrome-52 (V8=5.2.361.49), he notado que los parámetros nombrados están casi implementados, pero ese orden todavía tiene prioridad. No puedo encontrar lo que dice la norma ECMA.

>function f(a=1, b=2){ console.log(`a=${a} + b=${b} = ${a+b}`) }

> f()
a=1 + b=2 = 3
> f(a=5)
a=5 + b=2 = 7
> f(a=7, b=10)
a=7 + b=10 = 17

Pero se requiere orden!! Es el comportamiento estándar?

> f(b=10)
a=10 + b=2 = 12
 2
Author: jgran,
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
2016-08-18 09:17:47