¿Hay una mejor manera de hacer los parámetros de funciones opcionales en JavaScript?



13 Answers

Encuentro que esta es la forma más simple y más legible:

if (typeof myVariable === 'undefined') { myVariable = 'default'; }
//use myVariable here

La respuesta de Paul Dixon (en mi humilde opinión) es menos legible que esto, pero todo se reduce a las preferencias.

La respuesta de Insin es mucho más avanzada, ¡pero mucho más útil para grandes funciones!

EDITAR 17/11/2013 9:33 p.m .: Creé un paquete para Node.js que hace que sea más fácil "sobrecargar" funciones (métodos) llamadas parametric .

Question

Esta pregunta ya tiene una respuesta aquí:

Siempre he manejado parámetros opcionales en JavaScript como este:

function myFunc(requiredArg, optionalArg){
  optionalArg = optionalArg || 'defaultValue';

  // Do stuff
}

Hay una mejor manera de hacerlo?

¿Hay algún caso donde se use || como que va a fallar?




Si usa valores predeterminados de manera exhaustiva, esto parece mucho más legible:

function usageExemple(a,b,c,d){
    //defaults
    a=defaultValue(a,1);
    b=defaultValue(b,2);
    c=defaultValue(c,4);
    d=defaultValue(d,8);

    var x = a+b+c+d;
    return x;
}

Simplemente declare esta función en el escopeo global.

function defaultValue(variable,defaultValue){
    return(typeof variable!=='undefined')?(variable):(defaultValue);
}

Patrón de uso fruit = defaultValue(fruit,'Apple');

* PD: puede cambiar el nombre de la función defaultValue por un nombre corto, simplemente no use default , es una palabra reservada en javascript.




Te sugiero que uses ArgueJS esta manera:

function myFunc(){
  arguments = __({requiredArg: undefined, optionalArg: [undefined: 'defaultValue'})

  //do stuff, using arguments.requiredArg and arguments.optionalArg
  //    to access your arguments

}

También puede reemplazar undefined por el tipo de argumento que espera recibir, como este:

function myFunc(){
  arguments = __({requiredArg: Number, optionalArg: [String: 'defaultValue'})

  //do stuff, using arguments.requiredArg and arguments.optionalArg
  //    to access your arguments

}



Aquí está mi solución. Con esto puedes dejar cualquier parámetro que desees. El orden de los parámetros opcionales no es importante y puede agregar validación personalizada.

function YourFunction(optionalArguments) {
            //var scope = this;

            //set the defaults
            var _value1 = 'defaultValue1';
            var _value2 = 'defaultValue2';
            var _value3 = null;
            var _value4 = false;

            //check the optional arguments if they are set to override defaults...
            if (typeof optionalArguments !== 'undefined') {

                if (typeof optionalArguments.param1 !== 'undefined')
                    _value1 = optionalArguments.param1;

                if (typeof optionalArguments.param2 !== 'undefined')
                    _value2 = optionalArguments.param2;

                if (typeof optionalArguments.param3 !== 'undefined')
                    _value3 = optionalArguments.param3;

                if (typeof optionalArguments.param4 !== 'undefined')
                    //use custom parameter validation if needed, in this case for javascript boolean
                   _value4 = (optionalArguments.param4 === true || optionalArguments.param4 === 'true');
            }

            console.log('value summary of function call:');
            console.log('value1: ' + _value1);
            console.log('value2: ' + _value2);
            console.log('value3: ' + _value3);
            console.log('value4: ' + _value4);
            console.log('');
        }


        //call your function in any way you want. You can leave parameters. Order is not important. Here some examples:
        YourFunction({
            param1: 'yourGivenValue1',
            param2: 'yourGivenValue2',
            param3: 'yourGivenValue3',
            param4: true,
        });

        //order is not important
        YourFunction({
            param4: false,
            param1: 'yourGivenValue1',
            param2: 'yourGivenValue2',
        });

        //uses all default values
        YourFunction();

        //keeps value4 false, because not a valid value is given
        YourFunction({
            param4: 'not a valid bool'
        });



Si necesita lanzar un literal NULL , entonces podría tener algunos problemas. Aparte de eso, no, creo que probablemente estés en el camino correcto.

El otro método que algunas personas eligen es tomar una matriz de variables asociadas iterando a través de la lista de argumentos. Se ve un poco más ordenado, pero me imagino que es un poco (muy poco) poco más intenso de proceso / memoria.

function myFunction (argArray) {
    var defaults = {
        'arg1'  :   "value 1",
        'arg2'  :   "value 2",
        'arg3'  :   "value 3",
        'arg4'  :   "value 4"
    }

    for(var i in defaults) 
        if(typeof argArray[i] == "undefined") 
               argArray[i] = defaults[i];

    // ...
}



function Default(variable, new_value)
{
    if(new_value === undefined) { return (variable === undefined) ? null : variable; }
    return (variable === undefined) ? new_value : variable;
}

var a = 2, b = "hello", c = true, d;

var test = Default(a, 0),
test2 = Default(b, "Hi"),
test3 = Default(c, false),
test4 = Default(d, "Hello world");

window.alert(test + "\n" + test2 + "\n" + test3 + "\n" + test4);

http://jsfiddle.net/mq60hqrf/




Puedes usar algunos esquemas diferentes para eso. Siempre he probado argumentos.length:

function myFunc(requiredArg, optionalArg){
  optionalArg = myFunc.arguments.length<2 ? 'defaultValue' : optionalArg;

  ...

- Al hacerlo, posiblemente no puede fallar, pero no sé si tu camino tiene alguna posibilidad de fallar, ahora no puedo pensar en un escenario, donde en realidad podría fallar ...

¡Y luego Paul proporcionó un escenario fallido! -)




Esto es con lo que terminé:

function WhoLikesCake(options) {
  options = options || {};
  var defaultOptions = {
    a : options.a || "Huh?",
    b : options.b || "I don't like cake."
  }
  console.log('a: ' + defaultOptions.b + ' - b: ' + defaultOptions.b);

  // Do more stuff here ...
}

Llamado así:

WhoLikesCake({ b : "I do" });



No sé por qué la respuesta de @Paul es downvoted, pero la validación contra null es una buena opción. Tal vez un ejemplo más afirmativo tenga más sentido:

En JavaScript, un parámetro omitido es como una variable declarada que no se inicializa (solo var a1; ). Y el operador de igualdad convierte lo indefinido a nulo, por lo que funciona muy bien con los tipos de valores y objetos, y así es como CoffeeScript maneja los parámetros opcionales.

function overLoad(p1){
    alert(p1 == null); // Caution, don't use the strict comparison: === won't work.
    alert(typeof p1 === 'undefined');
}

overLoad(); // true, true
overLoad(undefined); // true, true. Yes, undefined is treated as null for equality operator.
overLoad(10); // false, false


function overLoad(p1){
    if (p1 == null) p1 = 'default value goes here...';
    //...
}

Sin embargo, existe la preocupación de que para la mejor semántica, typeof variable === 'undefined' sea ​​ligeramente mejor. No voy a defender esto ya que es la cuestión de la API subyacente cómo se implementa una función; no debería interesar al usuario API.

También debo agregar que esta es la única forma de asegurar físicamente que se omitió cualquier argumento, usando el operador in que desafortunadamente no funcionará con los nombres de los parámetros, así que tienen que pasar un índice de los arguments .

function foo(a, b) {
    // Both a and b will evaluate to undefined when used in an expression
    alert(a); // undefined
    alert(b); // undefined

    alert("0" in arguments); // true
    alert("1" in arguments); // false
}

foo (undefined);



Llegué a esta pregunta, buscando parámetros predeterminados en EcmaScript 2015 , mencionando así ...

Con ES6 podemos hacer los parámetros por defecto :

function doSomething(optionalParam = "defaultValue"){
    console.log(optionalParam);//not required to check for falsy values
}

doSomething(); //"defaultValue"
doSomething("myvalue"); //"myvalue"



Esos son más cortos que la versión del operador typeof.

function foo(a, b) {
    a !== undefined || (a = 'defaultA');
    if(b === undefined) b = 'defaultB';
    ...
}



function foo(requiredArg){
  if(arguments.length>1) var optionalArg = arguments[1];
}



Probé algunas opciones mencionadas aquí y el rendimiento las probó. En este momento el logicalor parece ser el más rápido. Aunque esto está sujeto a cambios a lo largo del tiempo (diferentes versiones de motor de JavaScript).

Estos son mis resultados (Microsoft Edge 20.10240.16384.0):

Function executed            Operations/sec     Statistics
TypeofFunction('test');          92,169,505     ±1.55%   9% slower
SwitchFuntion('test');            2,904,685     ±2.91%  97% slower
ObjectFunction({param1: 'test'});   924,753     ±1.71%  99% slower
LogicalOrFunction('test');      101,205,173     ±0.92%     fastest
TypeofFunction2('test');         35,636,836     ±0.59%  65% slower

Esta prueba de rendimiento se puede replicar fácilmente en: http://jsperf.com/optional-parameters-typeof-vs-switch/2

Este es el código de la prueba:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
    Benchmark.prototype.setup = function() {
        function TypeofFunction(param1, optParam1, optParam2, optParam3) {
            optParam1 = (typeof optParam1 === "undefined") ? "Some default" : optParam1;
            optParam2 = (typeof optParam2 === "undefined") ? "Another default" : optParam2;
            optParam3 = (typeof optParam3 === "undefined") ? "Some other default" : optParam3;
        }

        function TypeofFunction2(param1, optParam1, optParam2, optParam3) {
            optParam1 = defaultValue(optParam1, "Some default");
            optParam2 = defaultValue(optParam2, "Another default");
            optParam3 = defaultValue(optParam3, "Some other default");
        }

        function defaultValue(variable, defaultValue) {
            return (typeof variable !== 'undefined') ? (variable) : (defaultValue);
        }

        function SwitchFuntion(param1, optParam1, optParam2, optParam3) {
            switch (arguments.length - 1) { // <-- 1 is number of required arguments
                case 0:
                    optParam1 = 'Some default';
                case 1:
                    optParam2 = 'Another default';
                case 2:
                    optParam3 = 'Some other default';
            }
        }

        function ObjectFunction(args) {
            var defaults = {
                optParam1: 'Some default',
                optParam2: 'Another default',
                optParam3: 'Some other default'
            }
            args = $.extend({}, defaults, args);
        }

        function LogicalOrFunction(param1, optParam1, optParam2, optParam3) {
            optParam1 || (optParam1 = 'Some default');
            optParam2 || (optParam1 = 'Another default');
            optParam3 || (optParam1 = 'Some other default');
        }
    };
</script>



Con ES2015 / ES6 puede aprovechar Object.assign que puede reemplazar $.extend() o _.defaults()

function myFunc(requiredArg, options = {}) {
  const defaults = {
    message: 'Hello',
    color: 'red',
    importance: 1
  };

  const settings = Object.assign({}, defaults, options);

  // do stuff
}

También puede usar argumentos por defecto como este

function myFunc(requiredArg, { message: 'Hello', color: 'red', importance: 1 } = {}) {
  // do stuff
}



Related