2015-10-25 10 views
11

Ogni volta che creo una certa classe, ho bisogno di fare la stessa procedura noiosa:Una classe più breve inizializzazione in ECMAScript 6

class Something { 
    constructor(param1, param2, param3, ...) { 
    this.param1 = param1; 
    this.param2 = param2; 
    this.param3 = param3; 
    ... 
    } 
} 

C'è un modo per renderlo più elegante e più breve? Io uso Babel, quindi sono permesse alcune funzionalità sperimentali di ES7. Forse i decoratori possono aiutarti?

+2

Ha bisogno di essere un corso? puoi usare l'oggetto stenopeico letterale per assegnare i parametri come proprietà senza ripetere i loro nomi anche una sola volta. – Touffy

+1

Per rispondere alla tua ultima domanda, i decoratori non saranno di aiuto in quanto non possono essere collegati a un 'costruttore di classi – CodingIntrigue

risposta

10

È possibile utilizzare Object.assign:

class Something { 
    constructor(param1, param2, param3) { 
    Object.assign(this, {param1, param2, param3}); 
    } 
} 

Si tratta di una caratteristica ES2015 (aka ES6) che assegna i propri enumerabili proprietà di uno o più oggetti di origine ad un oggetto di destinazione.

Concesso, è necessario scrivere i nomi arg due volte, ma almeno è molto più breve, e se si stabilisce questo come idioma, lo gestisce bene quando si hanno argomenti che si desidera sull'istanza e altri che si don 't, ad esempio:

class Something { 
    constructor(param1, param2, param3) { 
    Object.assign(this, {param1, param3}); 
    // ...do something with param2, since we're not keeping it as a property... 
    } 
} 

esempio: (live copy on Babel's REPL):

class Something { 
    constructor(param1, param2, param3) { 
    Object.assign(this, {param1, param2, param3}); 
    } 
} 
let s = new Something('a', 'b', 'c'); 
console.log(s.param1); 
console.log(s.param2); 
console.log(s.param3); 

uscita:

 
a 
b 
c 
+0

Questo è abbastanza carino, ma stai ancora ripetendo i nomi param (una volta nella lista degli argomenti, una volta nella chiamata di assegnazione). È possibile clonare 'arguments', rimuovere proprietà predefinite e passarle a' assign'. – Touffy

+0

@Touffy: 'arguments' ha * named * proprietà per gli argomenti? Voglio dire, con nomi diversi da '0',' 1' e '2'? Questo è nuovo per me, se è così. –

+0

Hmmm proprio no. Suppongo che potresti analizzarli abbastanza facilmente dal toString della funzione, ma questo sta diventando brutto. – Touffy

3

Purtroppo, tutto ciò che puoi fare sono cose semplici come Object.assign, ma se stai cercando di rimuovere la ridondanza di digitare tutti i parametri due volte (una volta nella firma del costruttore e una volta nell'assegnazione) non c'è molto che tu possa fare.

Detto questo, potresti fare un trucco come questo. Anche se non sono sicuro che lo sforzo e il minimo di offuscamento che ne derivano ne valga la pena.

var dependencies = ['param1', 'param2', 'param3']; 

class Something { 
    constructor(...params) { 
    params.forEach((param, index) => this[dependencies[index]] = param); 
    } 
} 

var something = new Something('foo', 'bar', 'baz'); 
// something.param1 === 'foo' 

In questo modo si sta utilizzando un singolo array di nomi degli argomenti, quindi utilizzando la stessa matrice come un punto di riferimento quando si creano le proprietà dell'istanza di Something. Questo schema funzionerebbe bene in un'applicazione Angolare in cui si sta tentando di preservare i nomi delle dipendenze tramite la minimizzazione impostando la proprietà $inject.

Something.$inject = dependencies; 

PS - Benvenuti all'inferno ridondante di lingue classiche che ho pensato ho ricevuto da quando sono diventato uno sviluppatore JS: P

Onestamente, probabilmente si dovrebbe semplicemente usare un oggetto classico letterale meno hai davvero bisogno della formalità di una classe reale.

Modifica: Suppongo che si possa accettare un oggetto letterale nel costruttore se si desidera la semplicità di un letterale e la formalità di una classe effettiva.

class Something { 
    constructor(params) { 
    Object.keys(params).forEach((name) => this[name] = params[name]); 
    } 
} 

var something = new Something({ 
    param1: 'foo', 
    param2: 'bar', 
    param3: 'baz' 
}); 

Ma ora hai appena compiuto una classe in una classe dinamica che può essere istanziato con tutte le proprietà, un po 'come un oggetto letterale: P

solito Voglio una classe perché voglio formalizzare la oggetto e presentare un'API coerente e rigorosamente verificabile.

+1

Questo è brutto, ma ti darà i nomi degli argomenti della funzione in modo che tu possa scriverli nella definizione della funzione anziché in una matrice esterna: 'arguments.callee.toString(). Match (/^function. *? \ ((. *?) \) /) [1] .split (/, \ s * /)} ' – Touffy

+1

@Touffy Questo non funzionerebbe per me. La modalità Strict non mi consente di accedere a 'arguments.callee', quindi potresti aver bisogno di un [ulteriore soluzione] (http://stackoverflow.com/questions/29572466/how-do-you-find-out-the-caller- function-in-javascript-when-use-strict-is-enabled) per quella soluzione, rendendola ancora più brutta. – CodingIntrigue

1

È possibile creare un metodo statico all'interno di ciascuna classe che utilizza l'oggetto arguments e una matrice di nomi e restituisce un oggetto che può essere assegnato alla nuova istanza utilizzando Object.assign.

Verificare tramite Babel REPL.

class Something { 
    static buildArgs (ctx, args, paramNames) { 
    let obj = {} 
    Array.from(args).forEach(function (arg, i) { 
     let name = paramNames[i] || i 
     obj[name] = args[i] 
    }) 
    Object.assign(ctx, obj) 
    } 

    constructor() { 
    Something.buildArgs(this, arguments, [ 
     'param1', 
     'param2' 
    ]); 
    console.log(this) 
    } 
} 

new Something('one', 'two') 

Devo ammettere che l'aggiunta di un metodo buildArgs significa che questa soluzione non è più corta, ma il corpo del constructor è e abbiamo anche questi vantaggi:

  1. Si sta scrivendo solo i nomi dei parametri una volta .
  2. Sei protetto contro la minificazione.

Il codice di cui sopra ospita argomenti extra (i >= paramNames.length) tuttavia abbiamo potuto modificarlo se questo comportamento è indesiderato in modo tale che questi sono ancora analizzati, ma non assegnati all'istanza:

class Something { 
    static buildArgs (ctx, args, paramNames) { 
    let obj = {instance: {}, extra: {}} 
    Array.from(args).forEach(function (arg, i) { 
     let name = paramNames[i] || i 
     if (name) { 
      obj.instance[name] = args[i] 
     } else { 
      obj.extra[i] = args[i] 
     } 
    }) 
    Object.assign(ctx, obj) 
    } 

    constructor() { 
    let args = Something.buildArgs(this, arguments, ['param1', 'param2']); 
    // Do stuff with `args.extra` 
    } 
} 

o ignorato del tutto:

static buildArgs (args, paramNames) { 
    let obj = {} 
    Array.from(args).forEach(function (arg, i) { 
     let name = paramNames[i] 
     if (name) obj[name] = args[i] 
    }) 
    return obj 
    }