2012-06-23 8 views
5

Dla wygody użytkownika, mam funkcję, która najpierw otrzymuje opcjonalny argument, przed wymaganym argumentem. Na przykład:Opcjonalny argument pierwszy w JavaScript

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

Robię to raczej niż w następujący sposób ponieważ organizm zwrotna może być długie, a użytkownik może zapomnieć, aby zastąpić ustawienia domyślne dla opcjonalnych argumentów:

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

Obecnie robię to, by sprawdzić:

function myFunction(first, second) { 

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

pytania

  • Czy to właściwa droga?
  • Jak to zrobić, aby skalować, zwłaszcza jeśli wystąpiło N opcjonalnych argumentów, które są przed wymaganym argumentem?
+1

Dlaczego nie umieścić opcjonalnych argumentów na * końcu * listy argumentów? To jest bardziej użyteczne w JavaScript ze względu na semantykę '.bind()'. Bardziej "stabilne" argumenty powinny być na pierwszym miejscu, a te, które są bardziej prawdopodobne, mogą się różnić lub być nieobecne na końcu. – Pointy

+0

@Pointy Po prostu ciekawi mnie, jak biblioteki to robią i czy istnieje odpowiednia metoda. Na przykład, 'extend' jQuery ma flagę deepcopy, która jest opcjonalna umieszczona na pierwszej liście argumentów. – Joseph

+1

Co powiesz na użycie 'arguments [..]'? Czy iteracja przez tablicę i zatrzymanie się przy pierwszych argumentach typu [i] === 'function'' help? – SuperSaiyan

Odpowiedz

1

Nie trzeba podawać żadnego z parametrów. możesz po prostu powiedzieć:

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

I pobrać każdy parametr za pomocą argumentów [i]. Funkcja znajduje się w argumentach [arguments.length - 1].

Na drobne notatki, operator typeof zawsze zwraca ciąg znaków, więc == mogą być stosowane zamiast ===

3

To nie jest prawo sposób, ponieważ parametry opcjonalne są zwyczajowo umieszczane na zawsze koniec. I widzisz powód, dla którego: jest o wiele łatwiejsze w obsłudze. Gdyby długość funkcji anonimowej jest twoim problemem, klienci Twojego API powinien używać referencji funkcji lub zmiennych:

function callback1() { //... 

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

myFunction(callbackX, optional); 

Problem z ucieczki this można rozwiązać bind().

Jeśli naprawdę chcesz iść ścieżką wielu opcjonalnych parametrów i zwrotnego na końcu, mogę myśleć dwa sposoby: arguments obiekt lub zawijania wszystkie opcjonalne argumenty w jednym options obiektów.

Z arguments można napisać:

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; 
//... 

Zobacz jak brzydkie to jest w porównaniu do:

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

Z options owijarki przedmiotu zawsze masz dwa argumenty:

function myFunction(options, callback); 

Gdzie options to tylko obiekt:

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

Tak, używanie parametru obiektu opcji przed lub po funkcji zwrotnej jest dobrym rozwiązaniem. Ułatwia to przypisywanie wartości domyślnych za pomocą metody rozszerzania. –

0

Jeśli naprawdę chcesz przekazać wiele parametrów opcjonalnych pierwszy, można zrobić to tak:

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

ale to raczej brzydki.Myślę, że powinieneś po prostu umieścić opcjonalne rzeczy na końcu.

2

To można zrobić z bezproblemowej ArgueJS:

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

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

Bardzo podobny przykład można znaleźć w ArgueJS' first example. Należy pamiętać, że wymagane jest jedynie parametr w środku:

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

się jeszcze ładniejsza z 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); 
} 

Or CoffeeScript:

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