Quando dovrei creare automaticamente un object anche se “nuovo” è dimenticato?

Diciamo che ho il seguente costruttore di oggetti:

function Foo(bar) { this.bar = bar; } 

Se eseguo la funzione nello scope globale senza la new parola chiave, la bar verrà impostata in qualunque ambito venga chiamato Foo() :

 var foo = Foo(42); console.log(bar); // 42 console.log(foo.bar); // ERROR 

Quindi la mia idea è di fare qualcosa del genere:

 function Foo(bar) { if(!(this instanceof Foo)) { // return a Foo object return new Foo(bar); } this.bar = bar; } 

In questo modo, se faccio un new Foo(42) o Foo(42) , restituirebbe sempre un object Foo .

Questa è sempre una buona idea? Se sì, quando? Quando (e perché) sarebbe saggio evitare questa tecnica?

Questo può essere utile quando vuoi build internamente un object wrapper.

Una libreria poco conosciuta utilizza internamente questo approccio, jQuery.

Tuttavia non usano più l’approccio instanceof . Ogni volta che jQuery viene chiamato, lo fa automaticamente:

 // Define a local copy of jQuery jQuery = function( selector, context ) { // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); } 

Tutto ciò che fa internamente fa riferimento a questa copia locale. Alla fine del suo lavoro, lo collega quindi allo scopo globale

 window.jQuery = window.$ = jQuery; 

Quindi ogni volta che chiami $() utilizza internamente new . Si suppone anche che non si usi new esternamente, ma non importa se lo fai o no.


modificare

jsFiddle Demo

 //Foo entrance function Foo(bar){ //construct a new Foo object to return return new Foo.prototype.build(bar); } //constructor for returning new prototype Foo.prototype.build = function(bar){ //initialize base settings this.bar = bar; //chain object return this; }; //this is the complex part //tie the prototype of Foo.prototype.build.prototype to Foo.prototype //so that is "seems" like Foo is the parent of the prototype assignments //while allowing for the use of returning a new object in the Foo entrance Foo.prototype.build.prototype = Foo.prototype; //simple expansions at this point, nothing looks too different //makes a function for a constructed Foo that logs hello Foo.prototype.hello = function(){ console.log("Hello "+this.bar+" :)"); //returns this for chaining return this; }; //more extensions, this one changes this.bar's value Foo.prototype.setBar = function(arg){ //accesses the current Foo instance's .bar property this.bar = arg; //returns this for chianing return this; }; //should be seeing a pattern Foo.prototype.goodbye = function(){ console.log("Bye "+this.bar+" :("); return this; }; var foo = Foo(42); //console.log(bar); // ERROR console.log(foo.bar); // 42 foo.hello(); //Hello 42 :) foo.hello().setBar(9001).goodbye(); //Hello 42 :) Bye 9001 :( Foo(12).hello(); //Hello 12 :) 

Anche se non ho nulla contro quello stile, non lo userò personalmente solo per essere coerente. Certo, posso rendere tutti i miei costruttori come questo, ma mi sembra molto più codice da scrivere.

Se sono preoccupato di invocare accidentalmente un costruttore senza new , preferirei usare JSHint per avvertirmi. Vedi http://www.jshint.com/docs/options/#newcap .

Ho una buona ragione per evitarlo: se non ti stai dimenticando di usare il new , allora è solo un inutile sovraccarico. Potresti non accorgertene mai a meno che tu non stia creando migliaia di istanze di un dato object, ma è ancora lì.

Raccomando di limitare il numero di punti nel codice in cui viene eseguita la creazione di un new object: se non ci si basa su di esso ogni volta che si ha bisogno di un nuovo object, non ci si deve preoccupare di dimenticarlo in uno di essi . Ci sono numerosi modelli che possono aiutarti, ma il più semplice è semplicemente creare una funzione da qualche parte responsabile della creazione di oggetti e rimandarla ogni volta, indipendentemente dal fatto che crei 1 istanza o 1.000.000 di istanze.

Detto questo, se questo non ha senso, questo tipo di guardia è un ottimo ripiego. Si noti che presenta molte somiglianze con il modo in cui i costruttori di tipi built-in di JavaScript raddoppiano come convertitori di tipi quando vengono utilizzati senza new – quindi considererò i tipi definiti dall’utente come un luogo particolarmente adatto per utilizzare questa tecnica.

Vedi anche: la parola chiave “nuova” di JavaScript è considerata dannosa?

I sostenitori della “leggibilità” potrebbero descriverlo come un anti-modello, ma tu conosci il tuo codice meglio di chiunque altro – se funziona per te, allora fallo. Personalmente preferirei avere un solo modo di fare una cosa – lo rende chiaro e conciso. Se un altro sviluppatore entra nella mischia e cerca di usare il mio object senza creare un’istanza, preferirei lanciare un errore per dirgli che sono stupidi.