Parametri nominati in javascript

Trovo che i parametri denominati in C # siano abbastanza utili in alcuni casi.

calculateBMI(70, height: 175); 

Cosa succede se lo voglio in javascript ?


Quello che non voglio è –

 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 } 

Questo approccio ho già usato. C’è un altro modo?

Sto bene usando qualsiasi libreria per farlo. (Oppure qualcuno può indicarmi quello che già fa)

    ES2015

    In ES2015, è ansible utilizzare la destrutturazione dei parametri per simulare i parametri con nome. Richiede al chiamante di passare un object, ma puoi evitare tutti i controlli all’interno della funzione se usi anche i parametri predefiniti:

     myFunction({ param1 : 70, param2 : 175}); function myFunction({param1, param2}={}){ // ... } 

    ES5

    C’è un modo per avvicinarsi a ciò che si desidera, ma si basa sull’output di Function.prototype.toString [ES5] , che dipende in parte dall’implementazione, quindi potrebbe non essere compatibile con browser diversi.

    L’idea è di analizzare i nomi dei parametri dalla rappresentazione di stringa della funzione in modo che sia ansible associare le proprietà di un object al parametro corrispondente.

    Potrebbe quindi apparire una chiamata di funzione

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

    dove a e b sono argomenti posizionali e l’ultimo argomento è un object con argomenti denominati.

    Per esempio:

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

    Che useresti come:

     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

    Ci sono alcuni svantaggi di questo approccio (sei stato avvisato!):

    • Se l'ultimo argomento è un object, viene trattato come un "object argomento con nome"
    • Otterrai sempre tutti gli argomenti che hai definito nella funzione, ma alcuni potrebbero avere il valore undefined (che è diverso dall'assenza di valore). Ciò significa che non è ansible utilizzare arguments.length per verificare quanti argomenti sono stati passati.

    Invece di avere una funzione che crea il wrapper, potresti anche avere una funzione che accetta una funzione e vari valori come argomenti, come

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

    o addirittura estendere Function.prototype modo che tu possa fare:

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

    No , l’approccio ad oggetti è la risposta di JavaScript a questo. Non c’è alcun problema con questo a condizione che la tua funzione si aspetti un object piuttosto che parametri separati.

    Questo problema è stato un mio piccolo problema per un po ‘di tempo. Sono un programmatore esperto con molte lingue sotto la mia cintura. Una delle mie lingue preferite che ho avuto il piacere di usare è Python. Python supporta i parametri con nome senza alcun trucco …. Da quando ho iniziato ad usare Python (qualche tempo fa) tutto è diventato più semplice. Credo che ogni lingua dovrebbe supportare parametri denominati, ma non è questo il caso.

    Molte persone dicono di usare semplicemente il trucco “Passa un object” in modo da avere i parametri con nome.

     /** * My Function * * @param {Object} arg1 Named arguments */ function myFunc(arg1) { } myFunc({ param1 : 70, param2 : 175}); 

    E questo funziona benissimo, eccetto ….. quando si tratta della maggior parte degli IDE là fuori, molti di noi sviluppatori si basano su suggerimenti tipo / argomento all’interno del nostro IDE. Personalmente uso PHP Storm (insieme ad altri IDE JetBrains come PyCharm per Python e AppCode per Objective C)

    E il problema più grande nell’usare il trucco “Passa un object” è che quando chiami la funzione, l’IDE ti fornisce un suggerimento di tipo singolo e il gioco è fatto … Come dovremmo sapere quali parametri e tipi dovrebbero andare nel object arg1?

    Non ho idea di quali parametri dovrebbero andare in arg1

    Quindi … il trucco “Passa un object” non funziona per me … In realtà causa più mal di testa con dover guardare il docblock di ogni funzione prima di sapere quali parametri la funzione si aspetta … Certo, è fantastico per quando si mantiene il codice esistente, ma è orribile scrivere un nuovo codice.

    Bene, questa è la tecnica che uso … Ora potrebbero esserci dei problemi, e alcuni sviluppatori potrebbero dirmi che sto sbagliando, e ho una mente aperta quando si tratta di queste cose … Sono sempre disposto a guardare modi migliori per realizzare un compito … Quindi, se c’è un problema con questa tecnica, i commenti sono benvenuti.

     /** * My Function * * @param {string} arg1 Argument 1 * @param {string} arg2 Argument 2 */ function myFunc(arg1, arg2) { } var arg1, arg2; myFunc(arg1='Param1', arg2='Param2'); 

    In questo modo, ho il meglio di entrambi i mondi … nuovo codice è facile da scrivere in quanto il mio IDE mi dà tutti gli spunti di discussione appropriati … E, pur mantenendo il codice in seguito, posso vedere a colpo d’occhio, non solo il valore passato alla funzione, ma anche il nome dell’argomento. L’unico overhead che vedo è dichiarare i nomi degli argomenti come variabili locali per evitare di inquinare lo spazio dei nomi globale. Certo, è un po ‘di tipizzazione extra, ma banale rispetto al tempo necessario per cercare docblocks durante la scrittura di un nuovo codice o il mantenimento del codice esistente.

    Ora, ho tutti i parametri e tipi durante la creazione di un nuovo codice

    Se vuoi chiarire cosa sono ciascuno dei parametri, piuttosto che chiamare

     someFunction(70, 115); 

    perché non fare quanto segue

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

    certo, è una linea in più di codice, ma vince sulla leggibilità.

    Un altro modo sarebbe utilizzare gli attributi di un object adatto, ad esempio in questo modo:

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

    Ovviamente per questo esempio inventato è alquanto privo di significato. Ma nei casi in cui la funzione è simile

     do_something(some_connection_handle, some_context_parameter, some_value) 

    potrebbe essere più utile. Potrebbe anche essere combinato con l’idea “parameterfy” per creare un tale object da una funzione esistente in un modo generico. Questo è per ogni parametro che creerebbe un membro che può valutare una versione valutata parziale della funzione.

    Questa idea è ovviamente legata a Schönfinkeling aka Currying.

    C’è un altro modo. Se si passa un object per riferimento, le proprietà dell’object verranno visualizzate nell’ambito locale della funzione. So che questo funziona per Safari (non ho controllato altri browser) e non so se questa funzione ha un nome, ma l’esempio che segue ne illustra l’uso.

    Anche se in pratica non penso che questo offra alcun valore funzionale al di là della tecnica che stai già usando, è un po ‘più pulito semanticamente. E richiede ancora il passaggio di un riferimento a un object o di un object letterale.

     function sum({ a:a, b:b}) { console.log(a+'+'+b); if(a==undefined) a=0; if(b==undefined) b=0; return (a+b); } // will work (returns 9 and 3 respectively) console.log(sum({a:4,b:5})); console.log(sum({a:3})); // will not work (returns 0) console.log(sum(4,5)); console.log(sum(4)); 

    Trying Node-6.4.0 (process.versions.v8 = ‘5.0.71.60’) e Node Chakracore-v7.0.0-pre8 e poi Chrome-52 (V8 = 5.2.361.49), ho notato che i parametri con nome sono quasi implementato, ma quell’ordine ha ancora la precedenza. Non riesco a trovare quello che dice lo standard ECMA.

     >function f(a=1, b=2){ console.log(`a=${a} + b=${b} = ${a+b}`) } > f() a=1 + b=2 = 3 > f(a=5) a=5 + b=2 = 7 > f(a=7, b=10) a=7 + b=10 = 17 

    Ma l’ordine è richiesto !! È il comportamento standard?

     > f(b=10) a=10 + b=2 = 12