2012-06-23 13 views
5

Para facilidad del usuario, tengo una función que primero recibe un argumento opcional, antes del argumento requerido. Por ejemplo:Primero argumento opcional en JavaScript

ns.myFunction('optional arg', function(){ 
    //the required callback 
}); 

que estoy haciendo esto en lugar de hacer lo siguiente ya que el cuerpo de devolución de llamada podría ser larga, y el usuario podría olvidar a reemplazar los valores predeterminados de los argumentos opcionales:

ns.myFunction(function(){ 
    //the required callback 
}, 'optional arg'); 

Actualmente estoy haciendo esto para verificar:

function myFunction(first, second) { 

    //if second is undefined and first is a function 
    if (typeof second === 'undefined' && typeof first === 'function') { 
     second = first; 
    } 
} 

Preguntas

  • ¿Es esta la manera correcta?
  • ¿Cómo lo haría para que se amplíe, especialmente si había N argumentos opcionales que están antes del argumento requerido?
+1

¿Por qué no pondría los argumentos opcionales en el * end * de la lista de argumentos? Eso es más útil en JavaScript debido a la semántica de '.bind()'. Los argumentos más "estables" deberían ser los primeros, y los más propensos a variar o estar ausentes al final. – Pointy

+0

@Pointy Tengo curiosidad sobre cómo las bibliotecas lo hacen así y si había un método adecuado. Por ejemplo, 'extender' de jQuery tiene una bandera de copia profunda que es opcional situada en la primera lista de argumentos. – Joseph

+1

¿Qué le parece usar 'arguments [..]'? ¿No se iteraría a través de la matriz y se detendría en la primera ayuda 'typeof arguments [i] === function''? – SuperSaiyan

Respuesta

1

No es necesario nombrar ninguno de los parámetros. simplemente puede decir:

if (arguments.length < 2) // There are no optional parameters 

Y recupere cada parámetro mediante argumentos [i]. La función se encuentra en los argumentos [arguments.length - 1].

En una nota menor, el operador typeof devuelve siempre una cadena, por lo == se puede utilizar en lugar de ===

3

Ésta no es la correcta manera porque los parámetros son opcionales, por convención, siempre colocado en el fin. Y ve una razón por la cual: es mucho más fácil manejarlos. Si la longitud de la función anónima es su preocupación, clientes de su API deben utilizar referencias de las funciones o variables:

function callback1() { //... 

var callback2 = function() {//... 

myFunction(callbackX, optional); 

El problema con el escape this se puede resolver con bind().

Si realmente desea ir por la ruta de múltiples parámetros opcionales y devolución de llamada al final, puedo pensar en dos formas: objeto arguments o envolviendo todos los argumentos opcionales en un objetos options.

Con arguments puede escribir:

var lastOptionalIndex = arguments.length - 2; 
var callback = arguments[lastOptionalIndex + 1]; //required callback is always last 
var optionalFirst = lastOptionalIndex >=0? arguments[0] : undefined; 
var optionalSecond = lastOptionalIndex >=1? arguments[1] : undefined; 
//... 

Ver lo feo que se compara con:

function myFunction(callback, firstOptional, secondOptional //... 

Con options objeto contenedor que siempre tiene dos argumentos:

function myFunction(options, callback); 

Dónde options es solo un objeto:

{ 
    firstOptional: 1, 
    secondOptional: 'foo' 
    //... 
} 
+1

Sí, usar un parámetro de objeto de opciones antes o después de la función de devolución de llamada es una buena forma de hacerlo. Y hace que sea mucho más fácil asignar valores predeterminados utilizando el método extend. –

0

Si realmente desea pasar varios parámetros opcionales en primer lugar, usted podría hacerlo de esta manera:

function myFunction() { 
    alert("optional:"); 
    for(var i=0; i<arguments.length-1) alert(arguments[i]); 
    alert("function: "+ arguments[arguments.length-1]); 
} 

pero es bastante feo.Creo que deberías simplemente poner las cosas opcionales al final.

2

Se easilly se puede hacer con ArgueJS:

function myFunction(){ 
    arguments = __({first: [String], second: Function}) 

    // and now on, you can access your arguments by 
    // arguments.first and arguments.second 
} 

Un ejemplo muy similares se pueden encontrar en ArgueJS' first example. Tenga en cuenta que sólo se requiere el parámetro en el medio:

function range(){ 
    arguments = __({start: [Number, 0], stop: Number, step: [Number, 1]}) 

    for(var i = arguments.start; i < arguments.stop; i += arguments.step) 
    console.log(i); 
} 

repl:

>>> range(3) 
0 
1 
2 
>>> range(3, 5) 
3 
4 
>>> range(0, 5, 2) 
0 
2 
4 
0

ArgueJS:

function range(){ 
    arguments = __({start: [Number, 0], stop: Number, step: [Number, 1]}); 
    for(var i = arguments.start; i < arguments.stop; i += arguments.step) 
    console.log(i); 
} 

se vuelven aún más bonita con ES6:

function range(){ 
    {start, stop, step} = __({start: [Number, 0], stop: Number, step: [Number, 1]}) 
    for(var i = start; i < stop; i += step) 
    console.log(i); 
} 

O CoffeeScript:

range = -> 
    {start, stop, step} = __ {start: [Number, 0], stop: Number, step: [Number, 1]} 
    console.log i for i in [start...stop] by step 
Cuestiones relacionadas