2015-10-18 20 views
5

Ho usato la seguente funzione per creare istanze di classi sconosciute per un certo tempo:constr.apply (questo, args) nelle classi ES6

Kernel.prototype._construct = function (constr, args) { 
    function F() { 
    constr.apply(this, args); // EXCEPTION! 
    } 
    F.prototype = constr.prototype; 
    return new F(); 
}; 

Se uso prototipi tutto funziona:

function Person(name, surname) { 
    this.name = name; 
    this.surname = surname; 
} 

var person = Kernel._construct(Person, ["name", "surname"]); // WORKS! 

Tuttavia, alcune persone stanno usando la mia libreria usando ES6 classi native nel nodo v4 +:

class Person { 
    constructor(name, surname) { 
    this.name = name; 
    this.surname = surname; 
    } 
} 

var person = Kernel._construct(Person, ["name", surname]); // EXCEPTION! 

Essi sono sempre un errore:

TypeError: Class constructors cannot be invoked without 'new' 

Devo essere in grado di invocare il costruttore con un numero sconosciuto di argomenti. Qualche idea su come aggirare questo problema?

+0

Qual è lo scopo di '_bind' nel codice? – Amit

+0

L'ho rimosso, era lì perché stavo testando usando _bind ... –

risposta

9

Ci sono vari modi per farlo:

  1. Utilizzando metodi Function dell'oggetto:

    Kernel.prototype._construct = function (constr, args) { 
        return new (Function.prototype.bind.apply(constr, [null].concat(args))); 
    }; 
    

    Qui siamo applyingargs come argomenti per bind. L'obiettivo è avere una funzione che può essere chiamata senza arugmenti in modo che possiamo chiamare new x(). bind fa questo per noi, ma abbiamo bisogno di configurarlo correttamente. La sintassi è:

    func.bind(thisArg[, arg1][, args2...]) 
    // calling the results from the above is like 
    // thisArg.func(arg1, arg2...); 
    

    Vogliamo usare constr come la funzione di legare, e gli elementi args come argomenti. Non ci importa di thisArg. Per fare ciò, dobbiamo "convertire" l'array args in argomenti. La chiamata apply fa che:

    func.apply(thisArg[, args]); 
    // calling the results from the above is like 
    // thisArg.func(args[0], args[1]...); 
    

    apply è in realtà chiamando bind.Il primo elemento, [null], è importante perché vogliamo chiamare bind dove thisArg è null - come questo: constr.bind(null, args[0], args[1]...).

  2. Utilizzando ES2015 Spread operator:

    Kernel.prototype._construct = function (constr, args) { 
        return new constr(...args); 
    }; 
    

    Questo è molto più semplice, ma ci sono 2 problemi:

    1. Si richiede il supporto ES2015, e anche l'ultimo nodo (V4.2.1) richiede un flag per questo (--harmony_spreadcalls).
    2. Questo genererà un errore di sintassi se non è supportato e non è possibile nemmeno farlo in modo condizionale, quindi utilizzare lo script dinamico (eval()/new Function(...)), che non è consigliato.
  3. Utilizzo dell'oggetto Reflect incorporato.

    Kernel.prototype._construct = function (constr, args) { 
        return Reflect.construct(constr, args); 
    }; 
    

    Questo è anche semplice, ma è ancora più indietro in termini di supporto (in pratica si must uso Babel).

+0

Grazie per la tua brillante risposta, l'opzione uno ha risolto il mio problema :) –

+0

Va notato che 1 funzionerà solo per le classi native con native Implementazione di 'Function.prototype.bind'. Se viene accidentalmente sostituito con polyfill, TypeError verrà comunque lanciato (le classi transpiled sono ok con qualsiasi implementazione). – estus

+0

Ottima risposta. La soluzione 1 è davvero killer ed è stata solo un'opzione per me – Karamell

2

È possibile aggirare questo legandosi gli argomenti alla funzione e quindi chiamando nuovo su di esso:

Kernel.prototype._construct = function (constr, args) { 
    var F = constr.bind(null, args); 
    return new F(); 
}; 
+0

È una buona idea, ma la tua soluzione è imprecisa. Vedi la mia risposta. – Amit

2

Buona domanda, ma non, non si può chiamare costruttori senza la parola chiave new in ES6 in base alle ECMAScript §9.22.

ho chiesto un'interrogazione identica qui: ES6: call class constructor without new keyword

I need to be able to invoke the constructor with an unknown number of arguments. Any ideas about how to get around this issue?

Beh questo non dice nulla sulla (non) utilizzando la parola chiave new, ma si può facilmente usare argomenti variadic in un costruttore ES6

class Person { 
    constructor(name, surname, ...others) { 
    this.name = name; 
    this.surname = surname; 
    this.others = others || []; 
    } 
} 

let p = new Person('OweR', 'ReLoaDeD', 'a', 'b', 'c'); 
p; 
//=> {"name":"OweR","surname":"ReLoaDeD","others":["a","b","c"]} 

Sospetto che non sia quello che cerchi. Tuttavia, se si sta tentando di evitare l'uso di new per richiamare una classe in ES6, non è possibile.


Quindi, con ciò detto, cosa stai effettivamente cercando di realizzare?

+0

Sto lavorando nel supporto '--harmony' per http://inversify.io/ –