Ci sono usi legittimi per l'istruzione "with" di JavaScript?


369

I commenti di Alan tempesta in risposta alla mia risposta per quanto riguarda la withdichiarazione mi ha fatto pensare. Raramente ho trovato un motivo per utilizzare questa particolare funzione linguistica e non avevo mai pensato molto a come potesse causare problemi. Ora, sono curioso di sapere come potrei fare un uso efficace with, evitando le sue insidie.

Dove hai trovato withutile l' affermazione?


52
Non lo uso mai. È più facile vivere senza di essa se fingo che non esista.
Nosredna,

6
Una volta potrebbero esserci stati molti usi validi per questo. Ma è discutibile. ES5 Strict rimosso withquindi non c'è più nulla di simile.
Thomas Aylott,

27
Vale la pena notare che ES5 Strict è ancora facoltativo .
Shog9,

5
Invece di rimuovere 'with' in ES5 strict, non sarebbe stato meglio cambiare lo standard in modo che se non venisse trovata alcuna variabile, qualsiasi assegnazione fatta all'interno di 'with' è vincolata all'oggetto argomento?
JussiR,

2
@JussiR: Probabilmente. Ma il problema nel farlo è che potrebbe rompere le cose nei browser più vecchi.
Sune Rasmussen,

Risposte:


520

Un altro uso mi è venuto in mente oggi, quindi ho cercato il web con entusiasmo e ne ho trovato una menzione: Definire variabili all'interno di Block Scope .

sfondo

JavaScript, nonostante la sua somiglianza superficiale con C e C ++, non include le variabili al blocco in cui sono definite:

var name = "Joe";
if ( true )
{
   var name = "Jack";
}
// name now contains "Jack"

Dichiarare una chiusura in un ciclo è un'attività comune in cui ciò può causare errori:

for (var i=0; i<3; ++i)
{
   var num = i;
   setTimeout(function() { alert(num); }, 10);
}

Poiché il ciclo for non introduce un nuovo ambito, lo stesso num- con un valore di 2- sarà condiviso da tutte e tre le funzioni.

Un nuovo ambito: letewith

Con l'introduzione della letdichiarazione in ES6 , diventa facile introdurre un nuovo ambito quando necessario per evitare questi problemi:

// variables introduced in this statement 
// are scoped to each iteration of the loop
for (let i=0; i<3; ++i)
{
   setTimeout(function() { alert(i); }, 10);
}

O anche:

for (var i=0; i<3; ++i)
{
   // variables introduced in this statement 
   // are scoped to the block containing it.
   let num = i;
   setTimeout(function() { alert(num); }, 10);
}

Fino a quando ES6 non sarà universalmente disponibile, questo uso rimarrà limitato ai browser e agli sviluppatori più recenti che desiderano utilizzare i transpiler. Tuttavia, possiamo facilmente simulare questo comportamento utilizzando with:

for (var i=0; i<3; ++i)
{
   // object members introduced in this statement 
   // are scoped to the block following it.
   with ({num: i})
   {
      setTimeout(function() { alert(num); }, 10);
   }
}

Il ciclo ora funziona come previsto, creando tre variabili separate con valori compresi tra 0 e 2. Notare che le variabili dichiarate all'interno del blocco non hanno ambito, diversamente dal comportamento dei blocchi in C ++ (in C, le variabili devono essere dichiarate all'inizio di un blocco, quindi in un certo senso è simile). Questo comportamento è in realtà abbastanza simile a una letsintassi a blocchi introdotta nelle versioni precedenti dei browser Mozilla, ma non ampiamente adottata altrove.


15
Non ho mai pensato di usarlo con un valore letterale, sembra legittimo.
Matt Kantor il

81
Questo è davvero morto. Non ho mai pensato di giocare con l'ambito di JavaScript in questo modo. Ho completamente ampliato nuove aree alla mia codifica. Vorrei poter votare 10 volte!
kizzx2,

27
Per quelli ancora contrari, si potrebbe sempre usare una chiusura:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
In realtà, il problema sopra riportato appare sulla maggior parte dei browser non Mozilla (Chrome, Safari, Opera, IE).
Max Shawabkeh,

24
lasciare che il supporto per le dichiarazioni in IE salverebbe davvero la mia pancetta in questo momento, sto lottando con la mia coscienza sull'opportunità o meno di utilizzare con . Il vero problema è che anche con un with come let , è necessario prestare particolare attenzione a causa delle proprietà ereditate di un oggetto sulla catena del prototipo. Ad esempio var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };,. Nell'ambito dell'istruzione with , toString () è una proprietà ereditata di Object , quindi la funzione definita esplicitamente non viene chiamata. Comunque un'ottima risposta, comunque :-)
Andy E

161

Ho usato l'istruzione with come una semplice forma di importazione con ambito. Supponiamo che tu abbia un markup builder di qualche tipo. Invece di scrivere:

markupbuilder.div(
  markupbuilder.p('Hi! I am a paragraph!',
    markupbuilder.span('I am a span inside a paragraph')
  )
)

Potresti invece scrivere:

with(markupbuilder){
  div(
    p('Hi! I am a paragraph!',
      span('I am a span inside a paragraph')
    )
  )
}

Per questo caso d'uso, non sto facendo alcun compito, quindi non ho il problema di ambiguità associato a quello.


5
È così che l'ho visto usato in VB. (E l'unico uso di cui ero a conoscenza.)
Mateen Ulhaq,

2
Un aspetto negativo di questo sarebbe che se si fa riferimento a una variabile all'interno del blocco with che è al di fuori dell'oggetto markupbuilder, il motore js cercherà comunque prima all'interno del markupbuilder, riducendo le prestazioni.
Adam Thomas,

3
Questo aiuta davvero a ridurre il codice per coloro che lavorano con percorsi di tela.
Brian McCutchon,

4
La versione "con" di quel codice funziona letteralmente oltre 240 volte più lentamente sul mio computer rispetto alla versione "non con" dello stesso. Questo è il motivo per cui le persone dicono che non vi è alcun uso legittimo per questo. Non perché non possa rendere il codice più bello in alcuni punti. Vedi benchmark: jsfiddle.net/sc46eeyn
Jimbo Jonny il

1
@McBrainy - Questo è esattamente il tipo di posto in cui non dovresti usare il codice che esegue la moltitudine più lentamente (vedi il commento che ho appena fatto sopra questo). Se hai bisogno di scorciatoie per il codice super ripetuto puoi dichiararle. Ad esempio, se usi context.bezierCurveTocento volte di seguito puoi dirlo var bc2 = context.bezierCurveTo;e poi andare bc2(x,x,etc);ogni volta che vuoi chiamarlo. È abbastanza veloce e anche meno dettagliato, mentre withè super lento.
Jimbo Jonny,

83

Come indicato dai miei precedenti commenti, non credo che tu possa usare in withsicurezza, non importa quanto possa essere allettante in una determinata situazione. Poiché il problema non è direttamente trattato qui, lo ripeterò. Considera il seguente codice

user = {};
someFunctionThatDoesStuffToUser(user);
someOtherFunction(user);

with(user){
    name = 'Bob';
    age  = 20;
}

Senza indagare attentamente su tali chiamate di funzione, non c'è modo di sapere quale sarà lo stato del programma dopo l'esecuzione di questo codice. Se user.nameera già impostato, ora lo sarà Bob. Se non è stato impostato, il globale nameverrà inizializzato o modificato in Bobe l' useroggetto rimarrà senza aname proprietà.

Gli errori accadono. Se lo usi con te alla fine lo farai e aumenterai le possibilità che il tuo programma fallisca. Peggio ancora, potresti incontrare un codice funzionante che imposta un blocco globale con, deliberatamente o attraverso l'autore che non è a conoscenza di questa stranezza del costrutto. È un po 'come imbattersi in un interruttore, non hai idea se l'autore lo intendesse e non c'è modo di sapere se "correggere" il codice introdurrà una regressione.

I moderni linguaggi di programmazione sono pieni di funzioni. Alcune funzionalità, dopo anni di utilizzo, sono state scoperte come dannose e dovrebbero essere evitate. Javascript withè uno di questi.


18
Questo problema emerge solo quando si assegnano valori all'attributo dell'oggetto. Ma cosa succede se lo si utilizza solo per leggere i valori? Sostengo che va bene usarlo in quel caso.
airportyh,

10
Lo stesso problema si applica alla lettura dei valori Toby. Nel frammento di codice precedente non sai se il nome è impostato sull'oggetto utente, quindi non sapresti se stai leggendo il nome globale o il nome utente.
Alan Storm,

12
Con la lettura dei valori c'è una chiara regola di precedenza: gli attributi sull'oggetto vengono controllati prima delle variabili al di fuori dell'ambito. Questo non è diverso dall'ambito delle variabili nelle funzioni. Il vero problema con assegnazione e 'with', a quanto ho capito, sta nel fatto che l'attribuzione dell'attributo dipende dal fatto che l'attributo esista sull'oggetto corrente in questione, che è una proprietà di runtime e non può essere dedotto facilmente guardando il codice.
airportyh,

1
Penso che potresti essere proprio lì Toby. Il problema di scrittura mi basta per evitare completamente il costrutto.
Alan Storm,

"È un po 'come imbattersi in un interruttore, non ne hai idea ..." - Quindi allora vietiamo anche switch ()? ;-p
Sz.

66

withRecentemente ho trovato l' affermazione incredibilmente utile. Questa tecnica non mi è mai venuta in mente fino a quando non ho iniziato il mio progetto attuale: una console a riga di comando scritta in JavaScript. Stavo cercando di emulare le API della console Firebug / WebKit in cui è possibile immettere comandi speciali nella console ma non sovrascrivono alcuna variabile nell'ambito globale. Ci ho pensato quando ho cercato di superare un problema che ho menzionato nei commenti all'eccellente risposta di Shog9 .

Per ottenere questo effetto, ne ho usate due con istruzioni per "stratificare" un ambito dietro l'ambito globale:

with (consoleCommands) {
    with (window) {
        eval(expression); 
    }
}

La cosa grandiosa di questa tecnica è che, a parte gli svantaggi delle prestazioni, non subisce le solite paure withdell'affermazione, perché stiamo comunque valutando nell'ambito globale - non c'è pericolo che le variabili al di fuori del nostro pseudo-ambito siano modificato.

Sono stato ispirato a pubblicare questa risposta quando, con mia sorpresa, sono riuscito a trovare la stessa tecnica usata altrove - il codice sorgente di Chromium !

InjectedScript._evaluateOn = function(evalFunction, object, expression) {
    InjectedScript._ensureCommandLineAPIInstalled();
    // Surround the expression in with statements to inject our command line API so that
    // the window object properties still take more precedent than our API functions.
    expression = "with (window._inspectorCommandLineAPI) { with (window) { " + expression + " } }";
    return evalFunction.call(object, expression);
}

EDIT: Ho appena controllato la fonte Firebug, hanno incatenato 4 con istruzioni insieme per ancora più livelli. Pazzo!

const evalScript = "with (__win__.__scope__.vars) { with (__win__.__scope__.api) { with (__win__.__scope__.userVars) { with (__win__) {" +
    "try {" +
        "__win__.__scope__.callback(eval(__win__.__scope__.expr));" +
    "} catch (exc) {" +
        "__win__.__scope__.callback(exc, true);" +
    "}" +
"}}}}";

1
ma preoccuparti che ecmascript5 ti impedisca di farlo. Esiste una soluzione ecmascript 5?
kybernetikos

@Adam: non ne sono sicuro. ES5 genera solo un errore per questo in modalità rigorosa, quindi non è un problema immediato se non si dispone di una modalità rigorosa dichiarata a livello globale. ES Harmony potrebbe rappresentare un problema più grande, ma potrebbe essere risolvibile con alcune delle cose più recenti come i proxy.
Andy E

@AndyE mi dispiace che sia fuori tema, ma la tua 'console della riga di comando scritta in JavaScript' è disponibile ovunque?
Kybernetikos,

@Adam: no, non lo è. Il tutto doveva essere un set di strumenti per sviluppatori per Windows Desktop Gadgets, ma non l'ho mai finito (anche se la console funziona molto bene). Potrei finirlo ad un certo punto, anche se al momento i WDG non hanno un futuro molto brillante.
Andy E

3
Poche settimane fa abbiamo spostato l'implementazione della console in Chrome da con blocco a qualche simbolo magico perché con blocco bloccato alcune funzionalità ES6 :)
Alexey Kozyatinskiy

54

Sì, sì e sì. C'è un uso molto legittimo. Orologio:

with (document.getElementById("blah").style) {
    background = "black";
    color = "blue";
    border = "1px solid green";
}

Fondamentalmente qualsiasi altro hook DOM o CSS è un uso fantastico di with. Non è che "CloneNode" sarà indefinito e tornerà all'ambito globale a meno che tu non abbia fatto di tutto per decidere di renderlo possibile.

La lamentela sulla velocità di Crockford è che un nuovo contesto è stato creato da. I contesti sono generalmente costosi. Sono d'accordo. Ma se hai appena creato un div e non hai un framework a portata di mano per impostare il tuo css e devi impostare manualmente circa 15 proprietà CSS, allora la creazione di un contesto sarà probabilmente più economica della creazione di variabili e di 15 dereferenze:

var element = document.createElement("div"),
    elementStyle = element.style;

elementStyle.fontWeight = "bold";
elementStyle.fontSize = "1.5em";
elementStyle.color = "#55d";
elementStyle.marginLeft = "2px";

eccetera...


5
+1 come penso anche che ci siano molti usi legittimi di with. Tuttavia, in questo caso particolare potresti semplicemente fare:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree

5
Si potrebbe ottenere la stessa cosa in una linea utilizzando il semplice extendmetodo sia da jQuery o Underscore.js: $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham,

9
@TrevorBurnham - Se penserai che jQuery sia disponibile, utilizzeresti semplicemente il suo .css()metodo ...
nnnnnn,

4
Cosa impedisce esattamente a queste variabili di raggiungere l'ambito globale? È solo perché tutti gli stili CSS sono sempre definiti su tutti gli elementi o cosa?
Aprire l'

1
@Mark sì, sono sempre definiti, con valori nulli o stringhe vuoti come valori se non esiste uno stile personalizzato per una proprietà
Esailija

34

È possibile definire una piccola funzione di supporto per fornire i vantaggi withsenza l'ambiguità:

var with_ = function (obj, func) { func (obj); };

with_ (object_name_here, function (_)
{
    _.a = "foo";
    _.b = "bar";
});

8
OMG, LA MIA TESTA ESPLOSA! senza l'ambiguità? Devo votare così, amico!
Jarrod Dixon

@Jarrod: cosa c'è di così divertente in questo ( is.gd/ktoZ )? La maggior parte delle persone che usano questo sito è più intelligente di me, quindi perdonami se sbaglio, ma questa sembra una cattiva informazione.
corvo,

14
Ma questo è solo più lungo e più difficile da capire il modo di fare: var _ = nome_oggetto_qui; _.a = "foo"; _.b = "bar;
Rene Saarsoo

3
Rene: Detto questo, esponerai la variabile "_" allo scope esterno, causando potenziali bug. Esporrà anche tutte le variabili temporanee utilizzate nel calcolo dei parametri degli oggetti.
John Millikin,

30
Otterrai più bug with_dall'essere una versione fangosa raddoppiata (function(_){ _.a="foo"; })(object_here);(il modo standard per simulare blocchi in stile c / java). Usa quello invece.
mk.


18

Non ho mai usato con, non vedo un motivo e non lo consiglio.

Il problema withè che impedisce numerose ottimizzazioni lessicali che un'implementazione di ECMAScript può eseguire. Data l'ascesa di veloci motori basati su JIT, questo problema diventerà probabilmente ancora più importante nel prossimo futuro.

Potrebbe sembrare che withpermetta di costruire più puliti (quando, diciamo, introducendo un nuovo ambito invece di un comune wrapper di funzioni anonime o sostituendo alias verbose), ma non ne vale davvero la pena . Oltre a una riduzione delle prestazioni, c'è sempre il pericolo di assegnare a una proprietà di un oggetto sbagliato (quando la proprietà non viene trovata su un oggetto in ambito iniettato) e forse di introdurre erroneamente variabili globali. IIRC, quest'ultima questione è quella che ha motivato Crockford a raccomandare di evitare with.


6
La performance bogeyman viene tracciata frequentemente, quasi con la stessa frequenza dei globali ... Mi colpisce sempre come strano, dato che stiamo parlando di JavaScript . Supponeresti che il successo delle prestazioni sia davvero drammatico per giustificare tanta attenzione, ma ... Se hai dei numeri concreti sul costo di with(){}costrutti come quelli forniti in altre risposte qui, nei browser moderni, mi piacerebbe vedere loro!
Shog9,

6
Perché è strano nel contesto di Javascript? :) E sì, è drammatico. Pensaci: un'implementazione deve valutare un'espressione tra parentesi, convertirla in oggetto, inserirla nella parte anteriore della catena di portata corrente, valutare l'istruzione all'interno del blocco, quindi ripristinare la catena di portata alla normalità. È molto lavoro. Molto più di una semplice ricerca di proprietà che può essere trasformata in un codice di basso livello altamente ottimizzato. Ecco un benchmark molto semplice che ho appena fatto (fammi sapere se trovi errori) che dimostra la differenza - gist.github.com/c36ea485926806020024
kangax,

5
@kangax: Vengo da un background C ++, in cui è abbastanza tradizionale per molti programmatori ossessionare le piccole efficienze nel loro codice, anche quando in realtà non hanno un effetto evidente sulle prestazioni della routine o del programma più grandi. Mi sembra strano nel contesto di JavaScript, dove una parte così grande delle prestazioni di una routine può dipendere dall'implementazione della VM. Ho visto alcuni casi in cui i programmatori JS eviteranno, per esempio, una funzione anonima a causa di preoccupazioni sui costi di installazione, ma questa sembra essere l'eccezione non la regola, riservata ad aree di codice molto sensibili.
Shog9,

5
Detto questo, hai assolutamente ragione riguardo al costo di with(){}: impostare un nuovo ambito con withè enormemente costoso su ogni browser che ho testato. Dovresti evitarlo in qualsiasi codice chiamato molto frequentemente. Inoltre, Chrome ha mostrato un notevole successo per qualsiasi codice eseguito all'interno di un with()ambito. È interessante notare che IE aveva le migliori caratteristiche prestazionali per il codice all'interno di with()blocchi: fattorizzando i costi di installazione, with()fornisce i mezzi più veloci di accesso ai membri nelle macchine virtuali IE6 e IE8 (sebbene queste macchine virtuali siano le più lente in assoluto). Roba buona, grazie ...
Shog9,

5
FWIW: ecco la stessa serie di test con i costi di installazione fatturati: jsbin.com/imidu/edit L' accesso alle variabili with()è quasi un ordine di grandezza più lento in Chrome e due volte più veloce in IE ...!
Shog9,

13

Visual Basic.NET ha Withun'istruzione simile . Uno dei modi più comuni in cui lo uso è impostare rapidamente un numero di proprietà. Invece di:

someObject.Foo = ''
someObject.Bar = ''
someObject.Baz = ''

, Posso scrivere:

With someObject
    .Foo = ''
    .Bar = ''
    .Baz = ''
End With

Questa non è solo una questione di pigrizia. Inoltre rende il codice molto più leggibile. E a differenza di JavaScript, non soffre di ambiguità, poiché è necessario aggiungere un prefisso a tutto ciò che è interessato dall'istruzione con un .(punto). Quindi, i seguenti due sono chiaramente distinti:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

Il primo è someObject.Foo; quest'ultimo è Foonell'ambito esterno someObject .

Trovo che la mancanza di distinzione di JavaScript lo renda molto meno utile della variante di Visual Basic, poiché il rischio di ambiguità è troppo elevato. A parte questo, withè ancora un'idea potente che può migliorare la leggibilità.


2
Abbastanza vero. Non risponde alla sua domanda però. Quindi è fuori tema.
Allain Lalonde,

6
Anche questo mi passava per la mente. qualcuno doveva dirlo . Perché JavaScript non può avere solo il punto.
Carson Myers,

D'accordo, la notazione con i punti è superiore, vorrei che JavaScript la usasse. +1


7

L'uso di "con" può rendere il codice più secco.

Considera il seguente codice:

var photo = document.getElementById('photo');
photo.style.position = 'absolute';
photo.style.left = '10px';
photo.style.top = '10px';

Puoi asciugarlo come segue:

with(document.getElementById('photo').style) {
  position = 'absolute';
  left = '10px';
  top = '10px';
}

Immagino che dipenda dal fatto che tu abbia una preferenza per la leggibilità o l'espressività.

Il primo esempio è più leggibile e probabilmente consigliato per la maggior parte del codice. Ma la maggior parte del codice è piuttosto addomesticato comunque. Il secondo è un po 'più oscuro, ma usa la natura espressiva del linguaggio per ridurre la dimensione del codice e le variabili superflue.

Immagino che le persone a cui piace Java o C # sceglierebbero il primo modo (object.member) e coloro che preferiscono Ruby o Python sceglierebbero quest'ultimo.


Spiacenti, non mi ero reso conto che qualcuno aveva già pubblicato sostanzialmente lo stesso esempio un anno fa. A parte i problemi di prestazioni, "con" rende piacevole il codice DRY a spese di un po 'più difficile da leggere. Penso che per collaborazioni con altri sviluppatori o la maggior parte del codice di produzione, è buona norma evitare la parola chiave "with". Ma se stai lavorando con programmatori di livello esperto e capisci come evitare potenziali inefficienze, vai in città con "con".
Giona,

6

Penso che l'uso ovvio sia come scorciatoia. Se ad esempio stai inizializzando un oggetto, devi semplicemente salvare un sacco di "ObjectName". Un po 'come "con-slot" di Lisp che ti permette di scrivere

(with-slots (foo bar) objectname
   "some code that accesses foo and bar"

che è lo stesso della scrittura

"some code that accesses (slot-value objectname 'foo) and (slot-value objectname 'bar)""

È più ovvio perché questa è una scorciatoia quando la tua lingua consente "Objectname.foo" ma comunque.


1
bello vedere il codice lisp! Penso che "with" in javascript sia ovviamente ispirato dalle sue radici dello schema come lingua, ma purtroppo ti stai ridimensionando per pubblicare LISP su una domanda javascript.
Fire Crow,

1
Voto secondo il principio di base. 'with' è un costrutto straordinariamente potente. Ma la maggior parte delle persone di JS non capisce chiusure e scrive sistemi di ereditarietà di classe Java ridicolmente complessi su JS - quindi come possono essere consapevoli del potenziale di metaprogrammazione che offre "con"?
Jared

Naturalmente è with-slotsnecessario specificare quali slot si stanno utilizzando, mentre withsi utilizzerà qualunque slot che si rilevi durante l'esecuzione.
Samuel Edwin Ward,

6

Avendo esperienza con Delphi, direi che l'utilizzo con dovrebbe essere un'ottimizzazione delle dimensioni dell'ultima risorsa, possibilmente eseguita da un qualche tipo di algoritmo minimizer javascript con accesso all'analisi del codice statico per verificarne la sicurezza.

I problemi di scoping che puoi affrontare con un uso liberale della dichiarazione with possono essere un dolore reale in a ** e non vorrei che nessuno sperimentasse una sessione di debug per capire cosa sta succedendo nel tuo codice , solo per scoprire che ha catturato un membro oggetto o la variabile locale errata, anziché la variabile di ambito globale o esterna che si intendeva.

Il VB con istruzione è migliore, in quanto ha bisogno dei punti per chiarire l'ambiguità dello scoping, ma Delphi con istruzione è una pistola caricata con un grilletto e mi sembra che il javascript sia abbastanza simile da giustificare lo stesso avvertimento.


5
Il javascript con istruzione è peggiore di quello di Delphi. In Delphi, con si comporta altrettanto velocemente (se non più velocemente) della notazione object.member. In javascript, with deve percorrere l'ambito per verificare la presenza di membri corrispondenti, rendendolo sempre più lento della notazione object.member.
Martijn,

5

L'uso con non è raccomandato ed è vietato in modalità rigorosa ECMAScript 5. L'alternativa consigliata è assegnare l'oggetto di cui si desidera accedere a una variabile temporanea.

Fonte: Mozilla.org


4

L'istruzione with può essere utilizzata per ridurre la dimensione del codice o per i membri della classe privata, ad esempio:

// demo class framework
var Class= function(name, o) {
   var c=function(){};
   if( o.hasOwnProperty("constructor") ) {
       c= o.constructor;
   }
   delete o["constructor"];
   delete o["prototype"];
   c.prototype= {};
   for( var k in o ) c.prototype[k]= o[k];
   c.scope= Class.scope;
   c.scope.Class= c;
   c.Name= name;
   return c;
}
Class.newScope= function() {
    Class.scope= {};
    Class.scope.Scope= Class.scope;
    return Class.scope;
}

// create a new class
with( Class.newScope() ) {
   window.Foo= Class("Foo",{
      test: function() {
          alert( Class.Name );
      }
   });
}
(new Foo()).test();

L'istruzione with è molto utile se si desidera modificare l'ambito, ciò che è necessario per avere il proprio ambito globale che è possibile manipolare in fase di esecuzione. È possibile inserire costanti su di esso o alcune funzioni di supporto spesso utilizzate come ad esempio "toUpper", "toLower" o "isNumber", "clipNumber" ecc.

A proposito delle cattive prestazioni che leggo spesso: Scoping di una funzione non avrà alcun impatto sulle prestazioni, infatti nella mia FF una funzione con ambito funziona più velocemente di una senza ambito:

var o={x: 5},r, fnRAW= function(a,b){ return a*b; }, fnScoped, s, e, i;
with( o ) {
    fnScoped= function(a,b){ return a*b; };
}

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnRAW(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

s= Date.now();
r= 0;
for( i=0; i < 1000000; i++ ) {
    r+= fnScoped(i,i);
}
e= Date.now();
console.log( (e-s)+"ms" );

Quindi, nel modo sopra citato, l'istruzione with non ha alcun effetto negativo sulle prestazioni, ma è buona in quanto diminuisce la dimensione del codice, ciò che influisce sull'utilizzo della memoria sui dispositivi mobili.


3

L'uso di with rende anche il tuo codice più lento in molte implementazioni, poiché ora tutto viene racchiuso in un ambito extra per la ricerca. Non esiste un motivo legittimo per l'utilizzo con in JavaScript.


5
Ottimizzazione prematura. Non dichiarare "più lento" a meno che non abbia scricchiolato i numeri; qualsiasi sovraccarico è probabilmente banale su entrambi gli stili moderni e antichi.
mk.

2
Sono fortemente in disaccordo con le tue conclusioni, basate su ciò che potrebbe non essere un problema per sviluppatori diversi da te.
Dave Van den Eynde,

4
@mk: ok, crunching numerico per te qui: var obj={a:0,b:0,c:0};var d=+new Date;with(obj){for(var i=0;i<1000000;++i){a+=1;b+=1;c+=1}}+new Date-d;dà in media 2500, mentre var obj={a:0,b:0,c:0};var d=+new Date;for(var i=0;i<1000000;++i){obj.a+=1;obj.b+=1;obj.c+=1}+new Date-d;dà in media 750, rendendo quello che usa mentre più di 3 volte più lento.
yorick,

3
Ho appena eseguito questi in Chrome 23 nella console quando ho visto questo. I risultati che ho ottenuto sono stati 1138 per il withcodice e 903 senza. Con questa piccola differenza anche in un circuito ristretto, farei una selezione basata sulla semplicità di codifica e sulla facilità di refactoring caso per caso prima di preoccuparmi delle prestazioni.
Plynx,

3

Penso che l'istruzione with possa tornare utile quando si converte un linguaggio modello in JavaScript. Ad esempio JST in base2 , ma l'ho visto più spesso.

Sono d'accordo che si può programmare questo senza la dichiarazione with. Ma poiché non dà alcun problema, è un uso legittimo.


3

È utile per inserire un codice che viene eseguito in un ambiente relativamente complicato in un contenitore: lo uso per creare un'associazione locale per "finestra" e per eseguire codice destinato a un browser Web.


3

Penso che l'uso letterale dell'oggetto sia interessante, come una sostituzione drop-in per l'utilizzo di una chiusura

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       (function(info)
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       })(data[i]);
}

o la dichiarazione with equivalente a una chiusura

for(var i = nodes.length; i--;)
{
       // info is namespaced in a closure the click handler can access!
       with({info: data[i]})
       {           
            nodes[i].onclick = function(){ showStuff(info) };
       }        
}

Penso che il rischio reale sia la riduzione accidentale delle variabili che non fanno parte dell'istruzione with, motivo per cui mi piace che l'oggetto letterale venga passato, puoi vedere esattamente cosa sarà nel contesto aggiunto nel codice.


3

Ho creato una funzione di "unione" che elimina parte di questa ambiguità con l' withaffermazione:

if (typeof Object.merge !== 'function') {
    Object.merge = function (o1, o2) { // Function to merge all of the properties from one object into another
        for(var i in o2) { o1[i] = o2[i]; }
        return o1;
    };
}

Posso usarlo in modo simile a with, ma posso sapere che non influenzerà alcun ambito che non intendo che influisca.

Uso:

var eDiv = document.createElement("div");
var eHeader = Object.merge(eDiv.cloneNode(false), {className: "header", onclick: function(){ alert("Click!"); }});
function NewObj() {
    Object.merge(this, {size: 4096, initDate: new Date()});
}

3

Per alcuni pezzi di codice breve, vorrei usare le funzioni trigonometriche come sin, cosecc. In modalità gradi invece che in modalità radiante. A tale scopo, utilizzo un AngularDegreeoggetto:

AngularDegree = new function() {
this.CONV = Math.PI / 180;
this.sin = function(x) { return Math.sin( x * this.CONV ) };
this.cos = function(x) { return Math.cos( x * this.CONV ) };
this.tan = function(x) { return Math.tan( x * this.CONV ) };
this.asin = function(x) { return Math.asin( x ) / this.CONV };
this.acos = function(x) { return Math.acos( x ) / this.CONV };
this.atan = function(x) { return Math.atan( x ) / this.CONV };
this.atan2 = function(x,y) { return Math.atan2(x,y) / this.CONV };
};

Quindi posso usare le funzioni trigonometriche in modalità gradi senza ulteriore rumore della lingua in un withblocco:

function getAzimut(pol,pos) {
  ...
  var d = pos.lon - pol.lon;
  with(AngularDegree) {
    var z = atan2( sin(d), cos(pol.lat)*tan(pos.lat) - sin(pol.lat)*cos(d) );
    return z;
    }
  }

Ciò significa: utilizzo un oggetto come una raccolta di funzioni, che abilito in un'area di codice limitata per l'accesso diretto. Lo trovo utile.


non è una buona idea usare l' withistruzione in questo modo, rende il codice difficile da leggere perché non sai quale funzione è globale e quale funzione viene chiamata nell'ambito di with object, quindi se una qualsiasi funzione non è comunque definita nel l'ambito dell'oggetto tenterà quindi di accedervi nello spazio dei nomi globale
Saket Patel,

Essendo consapevole del problema di scoping, lo trovo ancora utile. Un matematico che legge il codice vuole vedere direttamente che la formula sopra è un'applicazione della "legge delle 4 parti successive" nella trigonometria sferica. La rigorosa alternativa offusca la formula: z = Math.atan2( Math.sin(d * Math.PI / 180), Math.cos( pol.lat * Math.PI / 180) * Math.tan( pos.lat * Math.PI / 180 ) - Math.sin( pol.lat * Math.PI / 180 ) * Math.cos( d * Math.PI / 180) ) * 180 / Math.PI;darebbe lo stesso risultato, ma è l'orrore.
rplantiko,

@rplantiko La cosa da tenere a mente è che la maggior parte delle persone non si sente a proprio agio con esso. Quindi, a meno che tu non stia scrivendo un codice che nessun altro potrà mai toccare. Inoltre, sono abbastanza sicuro di poterti mostrare un paio di utilizzi withche ti lascerebbero a bocca aperta.
Juan Mendes,

2

Penso che l'utilità di withpossa dipendere da quanto bene è scritto il tuo codice. Ad esempio, se stai scrivendo un codice che appare così:

var sHeader = object.data.header.toString();
var sContent = object.data.content.toString();
var sFooter = object.data.footer.toString();

quindi potresti sostenere che withmigliorerà la leggibilità del codice in questo modo:

var sHeader = null, sContent = null, sFooter = null;
with(object.data) {
    sHeader = header.toString();
    sContent = content.toString();
    sFooter = content.toString();
}

Al contrario, si potrebbe sostenere che stai violando la Legge di Demetra , ma, forse, forse no. Sto divagando =).

Soprattutto, sappi che Douglas Crockford consiglia di non utilizzare with. Vi esorto a consultare il suo post sul blog withe le sue alternative qui .


Grazie per la risposta, Tom. Ho letto la raccomandazione di Crockford e, sebbene abbia senso, va solo così lontano. Sto arrivando all'idea - toccata indirettamente da doekman - che il vero potere di with {} sta nel modo in cui può essere usato per manipolare l'ambito ...
Shog9

2

Semplicemente non vedo come l'utilizzo di with sia più leggibile della semplice digitazione di object.member. Non penso che sia meno leggibile, ma non penso nemmeno che sia più leggibile.

Come ha detto Lassevk, posso sicuramente vedere come l'utilizzo con sarebbe più soggetto a errori rispetto al semplice utilizzo della sintassi "object.member" molto esplicita.


1

È possibile visualizzare la convalida di un modulo in javascript su W3schools http://www.w3schools.com/js/js_form_validation.asp in cui il modulo oggetto viene "scansionato" per trovare un input con il nome "email"

Ma l'ho modificato per ottenere da QUALSIASI modulo tutti i campi convalidano come non vuoti, indipendentemente dal nome o dalla quantità di campo in un modulo. Beh, ho testato solo campi di testo.

Ma with () ha semplificato le cose. Ecco il codice:

function validate_required(field)
{
with (field)
  {
  if (value==null||value=="")
    {
    alert('All fields are mandtory');return false;
    }
  else
    {
    return true;
    }
  }
}

function validate_form(thisform)
{
with (thisform)
  {
    for(fiie in elements){
        if (validate_required(elements[fiie])==false){
            elements[fiie].focus();
            elements[fiie].style.border='1px solid red';
            return false;
        } else {elements[fiie].style.border='1px solid #7F9DB9';}
    }

  }
  return false;
}

1

La forcella Coco di CoffeeScript ha una withparola chiave, ma imposta semplicemente this(scrivibile come @in CoffeeScript / Coco) l'oggetto target all'interno del blocco. Ciò elimina le ambiguità e ottiene la conformità alla modalità rigorosa ES5:

with long.object.reference
  @a = 'foo'
  bar = @b

0

Ecco un buon uso per with: aggiungere nuovi elementi a un oggetto letterale, in base ai valori memorizzati in quell'oggetto. Ecco un esempio che ho appena usato oggi:

Avevo una serie di tessere possibili (con aperture rivolte in alto, in basso, a sinistra o a destra) che potevo usare e volevo un modo rapido per aggiungere un elenco di tessere che sarebbero sempre state posizionate e bloccate all'inizio del gioco . Non volevo continuare a digitare types.tbrper ogni tipo nell'elenco, quindi ho appena usato with.

Tile.types = (function(t,l,b,r) {
  function j(a) { return a.join(' '); }
  // all possible types
  var types = { 
    br:  j(  [b,r]),
    lbr: j([l,b,r]),
    lb:  j([l,b]  ),  
    tbr: j([t,b,r]),
    tbl: j([t,b,l]),
    tlr: j([t,l,r]),
    tr:  j([t,r]  ),  
    tl:  j([t,l]  ),  
    locked: []
  };  
  // store starting (base/locked) tiles in types.locked
  with( types ) { locked = [ 
    br,  lbr, lbr, lb, 
    tbr, tbr, lbr, tbl,
    tbr, tlr, tbl, tbl,
    tr,  tlr, tlr, tl
  ] } 
  return types;
})("top","left","bottom","right");

0

È possibile utilizzare con per evitare di dover gestire esplicitamente arity quando si utilizza require.js:

var modules = requirejs.declare([{
    'App' : 'app/app'
}]);

require(modules.paths(), function() { with (modules.resolve(arguments)) {
    App.run();
}});

Implementazione di requestjs.declare:

requirejs.declare = function(dependencyPairs) {
    var pair;
    var dependencyKeys = [];
    var dependencyValues = [];

    for (var i=0, n=dependencyPairs.length; i<n; i++) {
        pair = dependencyPairs[i];
        for (var key in dependencyPairs[i]) {
            dependencyKeys.push(key);
            dependencyValues.push(pair[key]);
            break;
        }
    };

    return {
        paths : function() {
            return dependencyValues;
        },

        resolve : function(args) {
            var modules = {};
            for (var i=0, n=args.length; i<n; i++) {
                modules[dependencyKeys[i]] = args[i];
            }
            return modules;
        }
    }   
}

0

Come ha sottolineato Andy E nei commenti della risposta di Shog9, questo comportamento potenzialmente inatteso si verifica quando si utilizza withcon un oggetto letterale:

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with ({num: i}) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "[object Object]"
  }
}

Non che un comportamento inaspettato non lo fosse già un segno distintivo di with.

Se vuoi davvero usare questa tecnica, almeno usa un oggetto con un prototipo nullo.

function scope(o) {
  var ret = Object.create(null);
  if (typeof o !== 'object') return ret;
  Object.keys(o).forEach(function (key) {
    ret[key] = o[key];
  });
  return ret;
}

for (var i = 0; i < 3; i++) {
  function toString() {
    return 'a';
  }
  with (scope({num: i})) {
    setTimeout(function() { console.log(num); }, 10);
    console.log(toString()); // prints "a"
  }
}

Ma questo funzionerà solo in ES5 +. Inoltre non usare with.


0

Sto lavorando a un progetto che consentirà agli utenti di caricare il codice per modificare il comportamento di parti dell'applicazione. In questo scenario, sto usando una withclausola per impedire al loro codice di modificare qualsiasi cosa al di fuori dell'ambito con cui voglio che si scherzino. La parte (semplificata) del codice che uso per fare questo è:

// this code is only executed once
var localScope = {
    build: undefined,

    // this is where all of the values I want to hide go; the list is rather long
    window: undefined,
    console: undefined,
    ...
};
with(localScope) {
    build = function(userCode) {
        eval('var builtFunction = function(options) {' + userCode + '}');
        return builtFunction;
    }
}
var build = localScope.build;
delete localScope.build;

// this is how I use the build method
var userCode = 'return "Hello, World!";';
var userFunction = build(userCode);

Questo codice garantisce (in qualche modo) che il codice definito dall'utente non abbia accesso a nessun oggetto con ambito globale come window né a nessuna delle mie variabili locali attraverso una chiusura.

Proprio come una parola per il saggio, devo ancora eseguire controlli del codice statico sul codice inviato dall'utente per assicurarmi che non stiano usando altri modi subdoli per accedere all'ambito globale. Ad esempio, il seguente codice definito dall'utente consente l'accesso diretto a window:

test = function() {
     return this.window
};
return test();


0

Mio

switch(e.type) {
    case gapi.drive.realtime.ErrorType.TOKEN_REFRESH_REQUIRED: blah
    case gapi.drive.realtime.ErrorType.CLIENT_ERROR: blah
    case gapi.drive.realtime.ErrorType.NOT_FOUND: blah
}

si riduce a

with(gapi.drive.realtime.ErrorType) {switch(e.type) {
    case TOKEN_REFRESH_REQUIRED: blah
    case CLIENT_ERROR: blah
    case NOT_FOUND: blah
}}

Puoi fidarti di un codice così di bassa qualità? No, vediamo che è stato reso assolutamente illeggibile. Questo esempio dimostra innegabilmente che non c'è bisogno di with-statement, se sto prendendo bene la leggibilità;)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.