466 Stimmen

Gibt es eine Möglichkeit, benannte Parameter in einem Funktionsaufruf in JavaScript anzugeben?

Ich finde die Funktion der benannten Parameter in C# in einigen Fällen sehr nützlich.

calculateBMI(70, height: 175);

Was kann ich verwenden, wenn ich dies in JavaScript möchte?


Was ich nicht will, ist dies:

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
}

Diesen Ansatz habe ich bereits verfolgt. Gibt es einen anderen Weg?

Ich habe kein Problem damit, eine beliebige Bibliothek dafür zu verwenden.

464voto

Felix Kling Punkte 751464

ES2015 und höher

In ES2015, Parameterentstrukturierung können zur Simulation von benannten Parametern verwendet werden. Dazu müsste der Aufrufer ein Objekt übergeben, aber Sie können alle Prüfungen innerhalb der Funktion vermeiden, wenn Sie auch Standardparameter :

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

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

// Or with defaults, 
function myFunc({
  name = 'Default user',
  age = 'N/A'
}={}) {
  // ...function body...
}

ES5

Es gibt eine Möglichkeit, dem gewünschten Ergebnis nahe zu kommen, aber sie basiert auf der Ausgabe von Function.prototype.toString [ES5] die bis zu einem gewissen Grad implementierungsabhängig ist, so dass sie möglicherweise nicht browserübergreifend kompatibel ist.

Die Idee ist, die Parameternamen aus der Zeichenkettendarstellung der Funktion zu analysieren, so dass Sie die Eigenschaften eines Objekts mit dem entsprechenden Parameter verknüpfen können.

Ein Funktionsaufruf könnte dann wie folgt aussehen

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

donde a y b sind Positionsargumente und das letzte Argument ist ein Objekt mit benannten Argumenten.

Zum Beispiel:

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

Die Sie als verwenden würden:

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

Es gibt einige Nachteile zu diesem Ansatz (Sie wurden gewarnt!):

  • Handelt es sich bei dem letzten Argument um ein Objekt, wird es als "benanntes Argument Objekte" behandelt.
  • Sie erhalten immer so viele Argumente, wie Sie in der Funktion definiert haben, aber einige von ihnen können den Wert undefined (das ist etwas anderes als gar keinen Wert zu haben). Das bedeutet, dass Sie nicht verwenden können arguments.length um zu prüfen, wie viele Argumente übergeben wurden.

Anstelle einer Funktion, die den Wrapper erstellt, könnten Sie auch eine Funktion haben, die eine Funktion und verschiedene Werte als Argumente akzeptiert, z. B.

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

oder sogar erweitern Function.prototype damit Sie das tun können:

foo.execute(a, b, {posArg: ...});

101voto

Mitya Punkte 32264

Nein - der Objektansatz ist die Antwort von JavaScript auf dieses Problem. Es gibt kein Problem damit, vorausgesetzt, Ihre Funktion erwartet ein Objekt und keine separaten Parameter.

53voto

Ray Perea Punkte 5242

Viele Leute sagen, man solle einfach den "Pass an object"-Trick anwenden, damit man benannte Parameter hat.

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

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

Und das funktioniert großartig, außer... wenn es um die meisten IDEs da draußen geht, verlassen sich viele von uns Entwicklern auf Typ-/Argument-Hinweise innerhalb unserer IDE. Ich persönlich verwende PhpStorm (zusammen mit anderen JetBrains-IDEs, wie PyCharm für Python und AppCode für Objektiv-C ).

Und das größte Problem bei der Verwendung des "Übergib ein Objekt"-Tricks ist, dass die IDE beim Aufruf der Funktion einen einzigen Typ-Hinweis gibt und das war's... Woher sollen wir wissen, welche Parameter und Typen in das arg1-Objekt eingehen sollen?

I have no idea what parameters should go in arg1

Also... der "Pass an object"-Trick funktioniert bei mir nicht... Er verursacht mehr Kopfschmerzen, weil ich mir den Docblock jeder Funktion ansehen muss, bevor ich weiß, welche Parameter die Funktion erwartet.... Sicher, es ist toll, wenn man bestehenden Code pflegt, aber es ist schrecklich, wenn man neuen Code schreibt.

Nun, dies ist die Technik, die ich verwende... Nun, es mag einige Probleme damit geben, und einige Entwickler mögen mir sagen, dass ich es falsch mache, und ich bin offen für diese Dinge... Ich bin immer bereit, nach besseren Möglichkeiten zu suchen, eine Aufgabe zu erfüllen... Wenn es also ein Problem mit dieser Technik gibt, dann sind Kommentare willkommen.

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

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

Auf diese Weise habe ich das Beste aus beiden Welten. Neuer Code ist leicht zu schreiben, da meine IDE mir alle richtigen Argumente anzeigt. Und wenn ich den Code später bearbeite, sehe ich auf einen Blick nicht nur den Wert, der an die Funktion übergeben wurde, sondern auch den Namen des Arguments. Der einzige Mehraufwand, den ich sehe, besteht darin, die Namen der Argumente als lokale Variablen zu deklarieren, um zu verhindern, dass der globale Namensraum verschmutzt wird. Sicher, es ist ein bisschen zusätzliche Tipparbeit, aber das ist trivial im Vergleich zu der Zeit, die man braucht, um Docblocks nachzuschlagen, während man neuen Code schreibt oder bestehenden Code pflegt.

Now, I have all the parameters and types when creating new code

Aktualisierung - 2022

JavaScript hat nun die Möglichkeit, mit Hilfe der in ES6 verfügbaren Objektdestrukturierung so etwas wie benannte Parameter zu verwenden. Die meisten neueren Browser können diese Funktion nutzen Siehe Browserunterstützung

Das funktioniert folgendermaßen:

// Define your function like this
function myFunc({arg1, arg2, arg3}) {
    // Function body
}

// Call your function like this
myFunc({arg1: "value1", arg2: "value2", arg3: "value3"})

// You can also have default values for arguments
function myFunc2({firstName, lastName, age = 21}) {
    // Function body
}

// And you can call it with or without an "age" argument
myFunc({firstName: "John", lastName: "Doe"}) // Age will be 21
myFunc({firstName: "Jane", lastName: "Doe", age: 22})

Das Beste daran ist, dass die meisten IDEs jetzt diese Syntax unterstützen und Sie eine gute Unterstützung für Argumentationshinweise erhalten

TypScript

Diejenigen unter Ihnen, die TypeScript verwenden, können das Gleiche mit dieser Syntax tun

function myFunc(
    {firstName, lastName, age = 21}:
    {firstName: string, lastName: string, age?: number}
) {
    // Function body
}

OR, über eine Schnittstelle

interface Params {
    firstName: string
    lastName: string
    age?: number
}

function myFunc({firstName, lastName, age = 21}: Params) {
    // Function body
}

29voto

dav_i Punkte 26308

Wenn Sie deutlich machen wollen, was die einzelnen Parameter sind, sollten Sie nicht einfach die

someFunction(70, 115);

tun Sie Folgendes:

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

Sicher, es ist eine zusätzliche Codezeile, aber sie ist besser lesbar.

7voto

Udo Klein Punkte 6578

Eine andere Möglichkeit wäre die Verwendung von Attributen eines geeigneten Objekts, z. B. so:

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

Natürlich ist das für dieses erfundene Beispiel ziemlich bedeutungslos. Aber in Fällen, in denen die Funktion aussieht wie

do_something(some_connection_handle, some_context_parameter, some_value)

könnte es nützlicher sein. Es könnte auch mit der "parameterfy"-Idee kombiniert werden, um ein solches Objekt aus einer bestehenden Funktion auf generische Weise zu erstellen. Das heißt, für jeden Parameter würde ein Mitglied erstellt, das eine teilweise ausgewertete Version der Funktion auswerten kann.

Diese Idee ist natürlich mit Schönfinkeling oder Currying verwandt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X