Wie lässt sich am besten feststellen, ob kein Argument an die JavaScript-Funktion gesendet wird?

236

Ich habe jetzt 2 Methoden gesehen, um festzustellen, ob ein Argument an eine JavaScript-Funktion übergeben wurde. Ich frage mich, ob eine Methode besser ist als die andere oder ob eine nur schlecht anzuwenden ist.

 function Test(argument1, argument2) {
      if (Test.arguments.length == 1) argument2 = 'blah';

      alert(argument2);
 }

 Test('test');

Oder

 function Test(argument1, argument2) {
      argument2 = argument2 || 'blah';

      alert(argument2);
 }

 Test('test');

Soweit ich das beurteilen kann, führen beide zu demselben Ergebnis, aber ich habe bisher nur das erste in der Produktion verwendet.

Eine weitere Option, wie von Tom erwähnt :

function Test(argument1, argument2) {
    if(argument2 === null) {
        argument2 = 'blah';
    }

    alert(argument2);
}

Laut Juans Kommentar wäre es besser, Toms Vorschlag zu ändern in:

function Test(argument1, argument2) {
    if(argument2 === undefined) {
        argument2 = 'blah';
    }

    alert(argument2);
}
Darryl Hein
quelle
Es ist wirklich das gleiche. Wenn Sie immer eine statische Anzahl von Argumenten haben, wählen Sie die zweite Methode. Andernfalls ist es wahrscheinlich einfacher, mit dem Argumentarray zu iterieren.
Luca Matteis
7
Ein Argument, das nicht übergeben wurde, wird als undefiniert eingestuft. Tests mit strikter Gleichheit gegen Null schlagen fehl. Sie sollten strikte Gleichheit mit undefiniert verwenden.
Juan Mendes
19
Denken Sie daran: argument2 || 'blah';in ‚blah‘ führen , wenn argument2ist false, nicht nur , wenn es nicht definiert ist (!). Wenn argument2es sich um einen Booleschen Wert handelt und die Funktion dafür übergeben falsewird, gibt diese Zeile trotz argument2korrekter Definition 'blah' zurück .
Sandy Gifford
9
@SandyGifford: Das gleiche Problem , wenn argument2ist 0, ''oder null.
Rvighne
@rvighne Sehr wahr. Die einzigartige Interpretation von Objekten und Casting in Javascript ist gleichzeitig das Beste und das Schlechteste.
Sandy Gifford

Antworten:

274

Es gibt verschiedene Möglichkeiten, um zu überprüfen, ob ein Argument an eine Funktion übergeben wurde. Zusätzlich zu den beiden, die Sie in Ihrer (ursprünglichen) Frage erwähnt haben - Überprüfen arguments.lengthoder Verwenden des ||Operators zum Bereitstellen von Standardwerten - kann man auch die Argumente explizit auf undefinedvia überprüfen argument2 === undefinedoder prüfen, typeof argument2 === 'undefined'ob man paranoid ist (siehe Kommentare).

Die Verwendung von ||Operator hat zum Standard geworden - alle coolen Kinder es tun - aber Vorsicht: Der Standardwert wird , wenn das Argument auswertet ausgelöst werden false, was bedeutet es eigentlich sein könnte undefined, null, false, 0, ''(oder irgendetwas anderes , für die Boolean(...)Rückkehr false).

Die Frage ist also, wann welcher Check verwendet werden soll, da alle leicht unterschiedliche Ergebnisse liefern.

Das Überprüfen arguments.lengthzeigt das "korrekteste" Verhalten, aber es ist möglicherweise nicht möglich, wenn es mehr als ein optionales Argument gibt.

Der Test für undefinedist der nächstbeste - er schlägt nur fehl, wenn die Funktion explizit mit einem undefinedWert aufgerufen wird, der aller Wahrscheinlichkeit nach genauso behandelt werden sollte wie das Weglassen des Arguments.

Die Verwendung des ||Operators kann die Verwendung des Standardwerts auslösen, selbst wenn ein gültiges Argument angegeben wird. Andererseits könnte sein Verhalten tatsächlich erwünscht sein.

Zusammenfassend: Verwenden Sie es nur, wenn Sie wissen, was Sie tun!

Meiner Meinung nach ||ist die Verwendung auch der richtige Weg, wenn es mehr als ein optionales Argument gibt und man kein Objektliteral als Problemumgehung für benannte Parameter übergeben möchte.

Eine andere gute Möglichkeit, Standardwerte mithilfe arguments.lengthvon bereitzustellen, besteht darin, die Beschriftungen einer switch-Anweisung durchzugehen:

function test(requiredArg, optionalArg1, optionalArg2, optionalArg3) {
    switch(arguments.length) {
        case 1: optionalArg1 = 'default1';
        case 2: optionalArg2 = 'default2';
        case 3: optionalArg3 = 'default3';
        case 4: break;
        default: throw new Error('illegal argument count')
    }
    // do stuff
}

Dies hat den Nachteil, dass die Absicht des Programmierers nicht (visuell) offensichtlich ist und "magische Zahlen" verwendet; es ist daher möglicherweise fehleranfällig.

Christoph
quelle
42
Sie sollten den Typ des Arguments2 === "undefined" überprüfen, falls jemand "undefined" definiert.
JW.
133
Ich werde einen Hinweis hinzufügen - aber welche kranken Bastarde machen solche Dinge?
Christoph
12
undefined ist eine Variable im globalen Raum. Es ist langsamer, diese Variable im globalen Bereich zu suchen als eine Variable in einem lokalen Bereich. Am schnellsten ist es jedoch, typeof x === "undefined" zu verwenden
etwa am
5
Interessant. Andererseits ist der Vergleich === undefined möglicherweise schneller als der Zeichenfolgenvergleich. Meine Tests scheinen jedoch darauf hinzudeuten, dass Sie Recht haben: x === undefined benötigt ~ 1,5x die Zeit des Typs von x === 'undefined'
Christoph
4
@Cristoph: Nachdem ich Ihren Kommentar gelesen hatte, fragte ich herum. Ich konnte beweisen, dass der String-Vergleich sicherlich nicht (nur) durch Zeigervergleich erfolgt, da der Vergleich eines gigantischen Strings länger dauert als ein kleiner String. Der Vergleich zweier gigantischer Zeichenfolgen ist jedoch nur dann sehr langsam, wenn sie mit Verkettung aufgebaut wurden, aber sehr schnell, wenn die zweite als Aufgabe aus der ersten erstellt wurde. Also funktioniert es wahrscheinlich durch einen Zeigertest, gefolgt von einem Buchstaben für Buchstaben-Test. Also, ja, ich war voller Mist :) Danke, dass du mich aufgeklärt hast ...
Juan Mendes
17

Wenn Sie jQuery verwenden, ist eine Option, die (insbesondere in komplizierten Situationen) hilfreich ist, die Erweiterungsmethode von jQuery .

function foo(options) {

    default_options = {
        timeout : 1000,
        callback : function(){},
        some_number : 50,
        some_text : "hello world"
    };

    options = $.extend({}, default_options, options);
}

Wenn Sie die Funktion aufrufen, gehen Sie wie folgt vor:

foo({timeout : 500});

Die Optionsvariable wäre dann:

{
    timeout : 500,
    callback : function(){},
    some_number : 50,
    some_text : "hello world"
};
Greg Tatum
quelle
15

Dies ist einer der wenigen Fälle, in denen ich den Test finde:

if(! argument2) {  

}

funktioniert ganz gut und trägt syntaktisch die richtige Implikation.

(Mit der gleichzeitigen Einschränkung, dass ich keinen legitimen Nullwert zulassen würde, der eine argument2andere Bedeutung hat; aber das wäre wirklich verwirrend.)

BEARBEITEN:

Dies ist ein wirklich gutes Beispiel für einen stilistischen Unterschied zwischen lose und stark typisierten Sprachen. und eine stilistische Option, die Javascript in Pik bietet.

Meine persönliche Präferenz (ohne Kritik für andere Präferenzen) ist Minimalismus. Je weniger der Code zu sagen hat, solange ich konsistent und prägnant bin, desto weniger muss jemand anderes verstehen, um meine Bedeutung richtig abzuleiten.

Eine Implikation dieser Präferenz ist, dass ich nicht - ich finde es nicht nützlich - eine Reihe von Typabhängigkeitstests stapeln möchte. Stattdessen versuche ich, den Code so zu gestalten, wie er aussieht. und teste nur für das, wofür ich wirklich testen muss.

Eine der Schwierigkeiten, die ich im Code einiger anderer Leute finde, besteht darin, herauszufinden, ob sie im größeren Kontext erwarten, tatsächlich auf die Fälle zu stoßen, auf die sie testen. Oder wenn sie versuchen, alles Mögliche zu testen, mit der Möglichkeit, dass sie den Kontext nicht vollständig genug antizipieren. Das bedeutet, dass ich sie am Ende gründlich in beide Richtungen aufspüren muss, bevor ich sicher etwas umgestalten oder modifizieren kann. Ich gehe davon aus, dass es eine gute Chance gibt, dass sie diese verschiedenen Tests durchgeführt haben, weil sie Umstände vorausgesehen haben, unter denen sie gebraucht würden (und die mir normalerweise nicht klar sind).

(Ich halte das für einen schwerwiegenden Nachteil bei der Verwendung dynamischer Sprachen durch diese Leute. Zu oft möchten die Leute nicht alle statischen Tests aufgeben und sie am Ende vortäuschen.)

Ich habe dies am deutlichsten beim Vergleich von umfassendem ActionScript 3-Code mit elegantem Javascript-Code gesehen. Der AS3 kann das 3- oder 4-fache des Hauptteils des js betragen, und die Zuverlässigkeit, die ich vermute, ist zumindest aufgrund der Anzahl (3-4X) der getroffenen Codierungsentscheidungen nicht besser.

Wie Sie sagen, Shog9, YMMV. : D.

dkretz
quelle
1
if (! argument2) argument2 = 'default' entspricht argument2 = argument2 || 'default' - Ich finde die zweite Version optisch ansprechender ...
Christoph
5
Und ich finde es ausführlicher und ablenkender; Aber es ist eine persönliche Präferenz, da bin ich mir sicher.
Dkretz
4
@le dorfier: Es schließt auch die Verwendung von leeren Zeichenfolgen, 0 und boolean false aus.
Shog9
@le dorfier: Über die Ästhetik hinaus gibt es einen entscheidenden Unterschied: Letzterer schafft effektiv einen zweiten Ausführungspfad, der unachtsame Betreuer dazu verleiten könnte, Verhalten hinzuzufügen, das über die einfache Zuweisung eines Standardwerts hinausgeht. YMMV natürlich.
Shog9
3
Was ist, wenn Parameter2 ein Boolescher Wert ist? === false; oder eine Funktion {return false;}?
Fresko
7

Es gibt signifikante Unterschiede. Lassen Sie uns einige Testfälle einrichten:

var unused; // value will be undefined
Test("test1", "some value");
Test("test2");
Test("test3", unused);
Test("test4", null);
Test("test5", 0);
Test("test6", "");

Bei der ersten Methode, die Sie beschreiben, verwendet nur der zweite Test den Standardwert. Die zweite Methode wird alle , aber die erste Standard (wie JS konvertiert undefined, null, 0, und ""in den boolean false. Und wenn Sie Tom Methode verwenden sind, nur der vierte Test den Standard verwenden!

Welche Methode Sie wählen, hängt wirklich von Ihrem beabsichtigten Verhalten ab. Wenn andere Werte als undefinedzulässig sind argument2, möchten Sie wahrscheinlich eine Variation des ersten. Wenn ein Wert ungleich Null, nicht Null und nicht leer gewünscht wird, ist die zweite Methode ideal - tatsächlich wird sie häufig verwendet, um einen so großen Wertebereich schnell aus der Betrachtung auszuschließen.

Shog9
quelle
7
url = url === undefined ? location.href : url;
Lamy
quelle
Nackte Knochen antworten. Eine Erklärung würde nicht schaden.
Paul Rooney
Es ist ein ternärer Operator, der kurz und bündig sagt: Wenn die URL undefiniert ist (fehlt), setzen Sie die URL-Variable auf location.href (die aktuelle Webseite), andernfalls setzen Sie die URL-Variable auf die definierte URL.
Rmooney
5

In ES6 (ES2015) können Sie Standardparameter verwenden

function Test(arg1 = 'Hello', arg2 = 'World!'){
  alert(arg1 + ' ' +arg2);
}

Test('Hello', 'World!'); // Hello World!
Test('Hello'); // Hello World!
Test(); // Hello World!

Andriy2
quelle
Diese Antwort ist wirklich interessant und könnte für op nützlich sein. Obwohl es die Frage nicht wirklich beantwortet
Ulysse BN
Wie ich sehe - er möchte arg definieren, wenn es nicht definiert wurde, also habe ich einige hilfreiche Informationen gepostet
Andriy2
Mein Punkt ist, dass Sie nicht antworten, wie Sie am besten feststellen können, ob ein Argument nicht an die JavaScript-Funktion gesendet wird . Die Frage könnte jedoch mit Standardargumenten beantwortet werden: Benennen Sie beispielsweise Ihre Argumente mit "default value"und prüfen Sie, ob der Wert tatsächlich ist "default value".
Ulysse BN
4

Es tut mir leid, ich kann immer noch keinen Kommentar abgeben, um Toms Antwort zu beantworten ... In Javascript (undefiniert! = Null) == false Tatsächlich funktioniert diese Funktion nicht mit "null". Sie sollten "undefiniert" verwenden.

Luca Matteis
quelle
1
Und Tom hat zwei positive Stimmen für eine falsche Antwort bekommen - es ist immer schön zu wissen, wie gut diese Community-Systeme funktionieren;)
Christoph
3

Warum nicht den !!Operator benutzen ? Dieser Operator, der vor der Variablen steht, wandelt sie in einen Booleschen Wert um (wenn ich das gut verstanden habe), sodass !!undefinedund !!null(und sogar !!NaN, was sehr interessant sein kann) zurückkehrenfalse .

Hier ist ein Beispiel:

function foo(bar){
    console.log(!!bar);
}

foo("hey") //=> will log true

foo() //=> will log false
RallionRl
quelle
Funktioniert nicht mit booleschen Zeichenfolgen true, zero und empty. Zum Beispiel foo (0); wird falsch protokollieren, aber foo (1) wird wahr protokollieren
rosell.dk
2

Es kann praktisch sein, sich der Argumenterkennung zu nähern, indem Sie Ihre Funktion mit einem Objekt mit optionalen Eigenschaften aufrufen:

function foo(options) {
    var config = { // defaults
        list: 'string value',
        of: [a, b, c],
        optional: {x: y},
        objects: function(param){
           // do stuff here
        }
    }; 
    if(options !== undefined){
        for (i in config) {
            if (config.hasOwnProperty(i)){
                if (options[i] !== undefined) { config[i] = options[i]; }
            }
        }
    }
}
1nfiniti
quelle
2

Es gibt auch eine schwierige Möglichkeit festzustellen, ob ein Parameter an eine Funktion übergeben wird oder nicht . Schauen Sie sich das folgende Beispiel an:

this.setCurrent = function(value) {
  this.current = value || 0;
};

Dies bedeutet, dass wenn der Wert von valuenicht vorhanden ist / übergeben wird, setzen Sie ihn auf 0.

Ziemlich cool, oder?

Mohammed Zameer
quelle
1
Dies bedeutet tatsächlich "wenn valueäquivalent zu false, setzen Sie es auf 0." Dies ist eine subtile, aber sehr wichtige Unterscheidung.
Charles Wood
@ CharlesWood Wert nicht bestanden / vorhanden bedeutet nur falsch
Mohammed Zameer
1
Sicher, wenn das den Anforderungen für Ihre Funktion entspricht. Wenn Ihr Parameter beispielsweise boolesch ist, sind trueund falsegültige Werte, und Sie möchten möglicherweise ein drittes Verhalten, wenn der Parameter überhaupt nicht übergeben wird (insbesondere, wenn die Funktion mehr als einen Parameter hat).
Charles Wood
1
Und ich sollte anerkennen, dass dies ein großes Argument in der Informatik ist und möglicherweise nur eine Ansichtssache ist: D
Charles Wood
@ CharlesWood Entschuldigung für die Verspätung auf der Party. Ich schlage vor, diese in der Antwort selbst mit Bearbeitungsoption hinzuzufügen
Mohammed Zameer
1

Manchmal möchten Sie möglicherweise auch nach Typ suchen, insbesondere wenn Sie die Funktion als Getter und Setter verwenden. Der folgende Code ist ES6 (läuft nicht in EcmaScript 5 oder älter):

class PrivateTest {
    constructor(aNumber) {
        let _aNumber = aNumber;

        //Privileged setter/getter with access to private _number:
        this.aNumber = function(value) {
            if (value !== undefined && (typeof value === typeof _aNumber)) {
                _aNumber = value;
            }
            else {
                return _aNumber;
            }
        }
    }
}
nbloqs
quelle
0
function example(arg) {
  var argumentID = '0'; //1,2,3,4...whatever
  if (argumentID in arguments === false) {
    console.log(`the argument with id ${argumentID} was not passed to the function`);
  }
}

Weil Arrays von erben Object.prototype. Betrachten Sie ⇑, um die Welt besser zu machen.

Angehoben
quelle
-1

fnCalledFunction (Param1, Param2, window.YourOptionalParameter)

Wenn die obige Funktion von vielen Stellen aufgerufen wird und Sie sicher sind, dass die ersten beiden Parameter von jedem Ort übergeben werden, Sie sich jedoch nicht sicher sind, was den dritten Parameter betrifft, können Sie window verwenden.

window.param3 wird behandelt, wenn es nicht in der Aufrufermethode definiert ist.

Goutam Panda
quelle