Existem usos legítimos para a declaração "with" do JavaScript?


369

Os comentários de Alan Storm em resposta à minha resposta sobre a withdeclaração me fizeram pensar. Eu raramente encontrei um motivo para usar esse recurso de idioma específico e nunca pensei muito em como ele poderia causar problemas. Agora, estou curioso para saber como posso fazer uso efetivo with, evitando as armadilhas.

Onde você achou a withdeclaração útil?


52
Eu nunca uso isso. É mais fácil viver sem ele se eu fingir que não existe.
Nosredna

6
Pode ter havido muitos usos válidos para isso. Mas é discutível. ES5 Rigoroso removido withpara que não exista mais tal coisa.
Thomas Aylott

27
Vale a pena notar aqui que o ES5 Strict ainda é opcional .
Shog9

5
Em vez de remover 'with' no ES5 estrito, não seria melhor alterar o padrão para que, se nenhuma variável for encontrada, qualquer atribuição feita dentro de 'with' seja vinculada ao objeto de argumento?
JussiR

2
@JussiR: Provavelmente. Mas o problema é que isso poderia quebrar as coisas em navegadores mais antigos.
Sune Rasmussen

Respostas:


520

Outro uso me ocorreu hoje, então procurei na web com entusiasmo e encontrei uma menção existente: Definindo Variáveis ​​no Escopo do Bloco .

fundo

O JavaScript, apesar de sua semelhança superficial com C e C ++, não define variáveis ​​para o bloco em que são definidas:

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

Declarar um fechamento em um loop é uma tarefa comum em que isso pode levar a erros:

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

Como o loop for não introduz um novo escopo, o mesmo num- com um valor de 2- será compartilhado pelas três funções.

Um novo escopo: letewith

Com a introdução da letdeclaração no ES6 , torna-se fácil introduzir um novo escopo quando necessário para evitar esses problemas:

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

Ou até:

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

Até que o ES6 esteja universalmente disponível, esse uso permanece limitado aos navegadores e desenvolvedores mais novos que desejam usar transpilers. No entanto, podemos simular facilmente esse comportamento usando 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);
   }
}

O loop agora funciona como pretendido, criando três variáveis ​​separadas com valores de 0 a 2. Observe que as variáveis ​​declaradas no bloco não têm escopo definido para ele, diferentemente do comportamento dos blocos em C ++ (em C, as variáveis ​​devem ser declaradas no início de um bloco, de modo que é semelhante). Esse comportamento é realmente muito semelhante a uma letsintaxe de bloco introduzida em versões anteriores dos navegadores Mozilla, mas não amplamente adotada em outros lugares.


15
Nunca pensei em usar com um literal, parece legítimo.
Matt Kantor

81
Isso é realmente muito morto. Eu nunca pensei em jogar com o escopo do JavaScript dessa maneira. Totalmente novas áreas expandidas para minha codificação. Eu gostaria de poder votar 10 vezes!
Kizzx2 22/06/09

27
Para os que ainda se opõem, pode-se sempre usar um fechamento:for (var i = 0; i < 3; ++i) { setTimeout ((function () { var num = i; return function () { alert (num); }; }) (), 10);}
Thomas Eding

4
Na verdade, o problema vinculado acima aparece na maioria dos navegadores não-Mozilla (Chrome, Safari, Opera, IE).
Max Shawabkeh

24
vamos apoio declaração no IE seria realmente salvar meu bacon agora, eu estou lutando com a minha consciência sobre se deve ou não usar com seu lugar. O verdadeiro problema é que, mesmo com a com um let , ainda é necessário um cuidado extra devido às propriedades herdadas de um objeto na cadeia de protótipos. Por exemplo var toString = function () { return "Hello"; }; with ({"test":1}) { console.log(toString()); };,. No escopo da instrução with , toString () é uma propriedade herdada de Object , portanto a função definida explicitamente não é chamada. Ainda uma grande resposta, embora :-)
Andy E

161

Eu tenho usado o com declaração como uma forma simples de importação com escopo. Digamos que você tenha algum tipo de construtor de marcações. Em vez de escrever:

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

Você poderia escrever:

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

Para este caso de uso, não estou realizando nenhuma atribuição, portanto não tenho o problema de ambiguidade associado a isso.


5
É assim que eu vi usado no VB. (E o único uso que eu estava ciente.)
Mateen Ulhaq

2
Uma desvantagem disso seria que, se você referenciar uma variável dentro do bloco with que está fora do objeto markupbuilder, o mecanismo js primeiro procurará por ela no markupbuilder de qualquer maneira, reduzindo o desempenho.
9788 Adam

3
Isso realmente ajuda a reduzir o código para quem trabalha com caminhos de tela.
Brian McCutchon

4
A versão "with" desse código literalmente roda 240 vezes mais devagar na minha máquina do que a versão "non-with" da mesma. É por isso que as pessoas dizem que não há uso legítimo para isso. Não porque não pode tornar o código mais bonito em alguns pontos. Veja benchmark: jsfiddle.net/sc46eeyn
Jimbo Jonny

11
@ McBrainy - Esse é exatamente o tipo de lugar em que você não deve usar um código que corra mais devagar (veja o comentário que acabei de fazer acima deste). Se você precisar de atalhos para códigos super repetidos, poderá declará-los. Por exemplo, se você usar context.bezierCurveTocem vezes seguidas, poderá dizer var bc2 = context.bezierCurveTo;e depois ir bc2(x,x,etc);sempre que quiser chamá-lo. Isso é muito rápido e menos detalhado, enquanto withé super lento.
Jimbo Jonny

83

Como meus comentários anteriores indicaram, não acho que você possa usar withcom segurança, por mais tentador que seja em qualquer situação. Como o problema não é abordado diretamente aqui, repetirei. Considere o seguinte código

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

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

Sem investigar cuidadosamente essas chamadas de função, não há como saber qual será o estado do seu programa após a execução desse código. Se user.namejá foi definido, será agora Bob. Se não foi definido, o global nameserá inicializado ou alterado para Bobe o userobjeto permanecerá sem uma namepropriedade.

Erros acontecem. Se você usar com você, eventualmente fará isso e aumentará as chances de seu programa falhar. Pior, você pode encontrar código de trabalho que define um global no bloco com, deliberadamente ou através do autor que não conhece essa peculiaridade da construção. É como encontrar falhas em um switch, você não tem idéia se o autor pretendeu isso e não há como saber se "consertar" o código introduzirá uma regressão.

As linguagens de programação modernas estão repletas de recursos. Alguns recursos, após anos de uso, são considerados ruins e devem ser evitados. Javascript withé um deles.


18
Esse problema só aparece quando você está atribuindo valores ao atributo do objeto. Mas e se você o estiver usando apenas para ler valores? Eu afirmo que não há problema em usá-lo nesse caso.
Airportyh 22/09/09

10
O mesmo problema se aplica à leitura dos valores Toby. No snippet de código acima, você não sabe se o nome está definido no objeto do usuário; portanto, não saberia se estava lendo o nome global ou o nome do usuário.
22711 Alan Storm

12
Com os valores de leitura, há uma regra de precedência clara: os atributos no objeto são verificados antes das variáveis ​​fora do escopo. Isso não é diferente das variáveis ​​com escopo nas funções. O verdadeiro problema com a atribuição e 'with', como eu o entendo, reside no fato de que a atribuição do atributo ocorre ou não, depende de o atributo existir no objeto atual em questão, que é uma propriedade de tempo de execução e não pode ser deduzido facilmente olhando o código.
Airportyh 22/09/09

11
Eu acho que você pode estar ali, Toby. O problema de gravação é suficiente para eu me esquivar completamente da construção.
23715 Alan Storm

"É como encontrar falhas em um switch, você não tem idéia ..." - Então, vamos proibir o switch () também? ;-p
Sz.

66

Na verdade, achei a withdeclaração incrivelmente útil recentemente. Essa técnica realmente nunca me ocorreu até que eu iniciei meu projeto atual - um console de linha de comando escrito em JavaScript. Eu estava tentando emular as APIs do console Firebug / WebKit, nas quais comandos especiais podem ser inseridos no console, mas eles não substituem nenhuma variável no escopo global. Pensei nisso ao tentar superar um problema mencionado nos comentários à excelente resposta de Shog9 .

Para alcançar esse efeito, usei duas instruções com "camada" de um escopo por trás do escopo global:

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

O melhor dessa técnica é que, além das desvantagens de desempenho, ela não sofre os medos usuais da withdeclaração, porque estamos avaliando no escopo global de qualquer maneira - não há perigo de variáveis ​​fora do nosso pseudo-escopo serem modificado.

Fiquei inspirado a postar esta resposta quando, para minha surpresa, consegui encontrar a mesma técnica usada em outros lugares - o código-fonte do 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: Apenas verifiquei a fonte do Firebug, eles encadearam 4 com instruções juntas para ainda mais camadas. Louco!

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);" +
    "}" +
"}}}}";

11
mas se preocupe que ecmascript5 o impeça de fazer isso. Existe uma solução ecmascript 5?
precisa saber é o seguinte

@ Adam: Não tenho certeza disso. O ES5 gera apenas um erro para isso no modo estrito; portanto, não é um problema imediato se você não tiver o modo estrito declarado globalmente. O ES Harmony pode representar um problema maior, mas pode ser corrigido com algumas das novidades, como proxies.
Andy E

@ Andy, desculpe, isso está fora de tópico, mas seu 'console de linha de comando escrito em JavaScript' está disponível em algum lugar?
precisa saber é o seguinte

@ Adam: não, não é. A coisa toda pretendia ser um conjunto de ferramentas de desenvolvedor para os gadgets da área de trabalho do Windows, mas eu nunca terminei (embora o console funcione muito bem). Posso terminar em algum momento, mesmo que os WDGs não tenham um futuro muito brilhante no momento.
Andy E

3
Poucas semanas atrás, nós mudamos a nossa aplicação consola no Chrome com bloqueio a algum símbolo mágico porque com bloco bloqueado alguns ES6 apresenta :)
Alexey Kozyatinskiy

54

Sim sim e sim. Existe um uso muito legítimo. Ver:

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

Basicamente, quaisquer outros ganchos DOM ou CSS são usos fantásticos com. Não é como se "CloneNode" fosse indefinido e retornasse ao escopo global, a menos que você se esforçasse e decidisse tornar isso possível.

A queixa de velocidade de Crockford é que um novo contexto é criado com. Os contextos são geralmente caros. Concordo. Mas se você acabou de criar uma div e não possui uma estrutura disponível para definir seu css e precisa configurar 15 ou mais propriedades CSS manualmente, criar um contexto provavelmente será mais barato que a criação de variáveis ​​e 15 dereferences:

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

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

etc ...


5
+1, pois também acho que há muitos usos legítimos de with. No entanto, neste caso em particular, você pode fazer o seguinte:element.style.cssText="background: black ; color: blue ; border: 1px solid green"
GetFree 20/08/10

5
Você poderia conseguir a mesma coisa em uma linha usando o simples extendmétodo de qualquer jQuery ou Underscore.js: $.extend(element.style, {fontWeight: 'bold', fontSize: '1.5em', color: '#55d', marginLeft: '2px'}).
Trevor Burnham

9
@TrevorBurnham - Se você estiver indo para assumir jQuery está disponível, você tinha acabado de usar o seu .css()método ...
nnnnnn

4
O que exatamente está impedindo que essas variáveis ​​cheguem ao escopo global? É apenas porque todos os estilos CSS são sempre definidos em todos os elementos, ou o quê?
MPEN

11
@ Marcos sim, eles são sempre definidas, com valores nulos ou cadeias vazias como valores, se não existe um estilo personalizado para uma propriedade
Esailija

34

Você pode definir uma função auxiliar pequena para fornecer os benefícios withsem a ambiguidade:

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

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

8
OMG, MINHA CABEÇA EXPLODIDA! sem a ambiguidade? Tenho que votar, cara!
Jarrod Dixon

@ Jarrod: o que há de tão engraçado nisso ( is.gd/ktoZ )? Quase todo mundo que usa este site é mais esperto do que eu, então me perdoe se eu estiver errado, mas isso parece uma informação ruim.
corvo

14
Mas isso é apenas mais longo e mais difícil de entender a maneira de fazer: var _ = obj_name_here; _.a = "foo"; _.b = "bar;
Rene Saarsoo

3
Rene: Com isso, você expõe a variável "_" ao escopo externo, resultando em possíveis erros. Ele também expõe quaisquer variáveis ​​temporárias usadas no cálculo de parâmetros de objetos.
John Millikin

30
Você obterá mais bugs por with_ser uma versão dupla e enlameada (function(_){ _.a="foo"; })(object_here);(a maneira padrão de simular blocos no estilo c / java). Use isso em vez disso.
mk.


18

Eu nunca uso com, não vejo um motivo e não o recomendo.

O problema withé que ele impede inúmeras otimizações lexicais que uma implementação do ECMAScript pode executar. Dado o surgimento de mecanismos rápidos baseados em JIT, esse problema provavelmente se tornará ainda mais importante no futuro próximo.

Pode parecer que withpermite construções mais limpas (quando, digamos, introduzir um novo escopo em vez de um invólucro de função anônima comum ou substituir um apelido detalhado), mas realmente não vale a pena . Além de um desempenho reduzido, sempre há o risco de atribuir a uma propriedade de um objeto errado (quando a propriedade não é encontrada em um objeto no escopo injetado) e talvez introduzir erroneamente variáveis ​​globais. IIRC, última questão que motivou Crockford a recomendar a evitar with.


6
O bicho-papão do desempenho é trotado com frequência, quase tão frequentemente quanto a coisa dos globais ... Sempre me parece estranho, já que estamos falando de JavaScript . Você presumiria que o resultado do desempenho é realmente dramático para garantir tanta atenção, mas ... Se você tiver números concretos sobre o custo de with(){}construções como as fornecidas em outras respostas aqui, em navegadores modernos, eu adoraria ver eles!
Shog9

6
Por que é estranho no contexto de Javascript? :) E sim, é dramático. Pense nisso - uma implementação precisa avaliar uma expressão entre parênteses, convertê-la em objeto, inseri-la na frente da cadeia de escopo atual, avaliar a instrução dentro do bloco e restaurar a cadeia de escopo de volta ao normal. Isso dá muito trabalho. Muito mais do que uma simples pesquisa de propriedades que pode ser transformada em um código de baixo nível altamente otimizado. Aqui é um ponto de referência muito simples Eu só fiz (deixe-me saber se você encontrar algum erro), demonstrando a diferença - gist.github.com/c36ea485926806020024
kangax

5
@kangax: Eu venho de um background em C ++, onde é tradicional para muitos programadores ficarem obcecados com pequenas eficiências em seu código, mesmo quando eles realmente não têm um efeito perceptível no desempenho de uma rotina ou programa maior. Parece-me estranho no contexto do JavaScript, onde uma parte tão grande do desempenho de uma rotina pode depender da implementação da VM. Já vi alguns casos em que os programadores JS evitarão, digamos, uma função anônima devido a preocupações com o custo de instalação, mas essa parece ser a exceção e não a regra, reservada para áreas muito sensíveis do código.
Shog9

5
Dito isso, você está absolutamente correto com relação ao custo de with(){}: configurar um novo escopo com withum custo enorme em todos os navegadores que testei. Você deseja evitar isso em qualquer código chamado com muita frequência. Além disso, o Chrome exibiu um sucesso dramático para qualquer código executado dentro de um with()escopo. Curiosamente, o IE tinha as melhores características de desempenho para o código dentro de with()blocos: calcular o custo de instalação, with()fornece os meios mais rápidos de acesso de membros nas VMs do IE6 e IE8 (embora essas VMs sejam as mais lentas no geral). Coisas boas, obrigado ...
Shog9 27/09/09

5
FWIW: aqui está o mesmo conjunto de testes com custos de instalação calculados: jsbin.com/imidu/edit O acesso variável para with()é quase uma ordem de magnitude mais lenta no Chrome e duas vezes mais rápida no IE ...!
Shog9

13

Visual Basic.NET tem uma Withdeclaração semelhante . Uma das maneiras mais comuns de uso é definir rapidamente várias propriedades. Ao invés de:

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

, Eu consigo escrever:

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

Isso não é apenas uma questão de preguiça. Também contribui para um código muito mais legível. E, diferentemente do JavaScript, ele não sofre ambiguidade, pois você precisa prefixar tudo que é afetado pela instrução com um .(ponto). Portanto, os dois seguintes são claramente distintos:

With someObject
    .Foo = ''
End With

vs.

With someObject
    Foo = ''
End With

O primeiro é someObject.Foo; o último está Foono escopo externo someObject .

Acho que a falta de distinção do JavaScript o torna muito menos útil do que a variante do Visual Basic, pois o risco de ambiguidade é muito alto. Fora isso, withainda é uma idéia poderosa que pode melhorar a legibilidade.


2
É verdade. Mas não responde a sua pergunta. Portanto, está fora de tópico.
Allain Lalonde

6
Isso estava passando pela minha mente também. alguém tinha que dizer isso . Por que o JavaScript não pode ter apenas o ponto também.
Carson Myers

Concordado, a notação de ponto é superior, desejo que o JavaScript a tenha usado. 1


7

Usar "com" pode tornar seu código mais seco.

Considere o seguinte código:

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

Você pode secá-lo da seguinte maneira:

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

Eu acho que depende se você tem preferência por legibilidade ou expressividade.

O primeiro exemplo é mais legível e provavelmente recomendado para a maioria dos códigos. Mas a maioria dos códigos é bem manso de qualquer maneira. O segundo é um pouco mais obscuro, mas usa a natureza expressiva da linguagem para reduzir o tamanho do código e as variáveis ​​supérfluas.

Eu imagino que pessoas que gostam de Java ou C # escolheriam a primeira maneira (object.member) e aquelas que preferem Ruby ou Python escolheriam a segunda.


Opa, eu não percebi que alguém já postou basicamente esse mesmo exemplo há um ano. Problemas de desempenho à parte, "com" cria um bom código DRY às custas de ser um pouco mais difícil de ler. Eu acho que para colaborações com outros desenvolvedores ou com a maioria dos códigos de produção, é uma boa prática evitar a palavra-chave "with". Mas se você estiver trabalhando com programadores de nível especializado e entender como evitar possíveis ineficiências, vá para a cidade com "with".
Jonah

6

Eu acho que o uso óbvio é como um atalho. Se você está, por exemplo, inicializando um objeto, basta salvar digitando muito "ObjectName". Como os "com slots" do lisp, que permitem escrever

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

que é o mesmo que escrever

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

É mais óbvio por que esse é um atalho quando seu idioma permite "Objectname.foo", mas ainda assim.


11
ótimo ver código lisp! Eu acho que "with" em javascript é obviamente inspirado por suas raízes de esquema como uma linguagem, mas, infelizmente, você foi prejudicado por postar LISP em uma questão de javascript.
Fogo Corvo

11
Voto positivo no princípio básico. 'with' é uma construção fenomenalmente poderosa. Mas a maioria das pessoas JS não entende fechamentos e escreve sistemas de herança de classe Java ridiculamente complexos sobre JS - então como eles podem estar cientes do potencial de metaprogramação que 'with' oferece?
jared

Obviamente, é with-slotsnecessário especificar quais slots você está usando, ao passo withque usará os slots que forem vinculados no tempo de execução.
Samuel Edwin Ward

6

Tendo experiência com o Delphi, eu diria que usar com deve ser uma otimização de tamanho de último recurso, possivelmente realizada por algum tipo de algoritmo minimizador de javascript com acesso à análise de código estático para verificar sua segurança.

Os problemas de escopo em que você pode entrar com o uso liberal da declaração with podem ser uma dor real no a ** e eu não gostaria que alguém experimentasse uma sessão de depuração para descobrir o que ele está acontecendo no seu código , apenas para descobrir que ele capturou um membro do objeto ou a variável local incorreta, em vez da variável de escopo global ou externa que você pretendia.

O VB com declaração é melhor, na medida em que precisa dos pontos para desambiguar o escopo, mas o Delphi with é uma arma carregada com um gatilho, e parece-me que o javascript é semelhante o suficiente para garantir o mesmo aviso.


5
O javascript com instrução é pior que o Delphi. No Delphi, com executa tão rápido (se não mais rápido) quanto a notação object.member. Em javascript, with tem que percorrer o escopo para verificar se há membros correspondentes, tornando-o sempre mais lento que a notação object.member.
287

5

O uso com não é recomendado e é proibido no modo estrito do ECMAScript 5. A alternativa recomendada é atribuir o objeto cujas propriedades você deseja acessar a uma variável temporária.

Fonte: Mozilla.org


4

A instrução with pode ser usada para diminuir o tamanho do código ou para membros da classe privada, exemplo:

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

A declaração with é muito útil se você deseja modificar o escopo, o que é necessário para ter seu próprio escopo global que você pode manipular em tempo de execução. Você pode colocar constantes nele ou certas funções auxiliares frequentemente usadas como, por exemplo, "toUpper", "toLower" ou "isNumber", "clipNumber" também.

Sobre o mau desempenho que eu leio com frequência: o escopo de uma função não terá nenhum impacto no desempenho. De fato, no meu FF, uma função de escopo é executada mais rapidamente do que uma sem escopo:

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

Portanto, da maneira mencionada acima, a declaração with não tem efeito negativo no desempenho, mas é bom, pois diminui o tamanho do código, o que afeta o uso da memória em dispositivos móveis.


3

O uso com também torna seu código mais lento em muitas implementações, pois agora tudo é envolvido em um escopo extra de pesquisa. Não há motivo legítimo para usar com JavaScript.


5
Otimização prematura. Não reivindique "mais devagar" a menos que você troque os números; qualquer sobrecarga provavelmente é trivial para os js modernos e antigos.
mk.

2
Discordo totalmente da sua conclusão, com base no que pode não ser um problema para outros desenvolvedores que você.
Dave Van den Eynde

4
@mk: ok, trituração de números para você aqui: 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á em média 2500, enquanto 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á em média 750, tornando o uso mais de 3 vezes mais lento.
23811 yorick

3
Acabei de executá-los no Chrome 23 no console quando vi isso. Os resultados obtidos foram 1138 para o withcódigo e 903 sem. Com essa pequena diferença, mesmo em um circuito fechado, eu faria uma seleção baseada na simplicidade da codificação e na facilidade de refatoração caso a caso, antes de me preocupar com o desempenho.
Plynx

3

Eu acho que a declaração with pode ser útil ao converter uma linguagem de modelo em JavaScript. Por exemplo, JST na base2 , mas já o vi com mais frequência.

Concordo que se pode programar isso sem a declaração with. Mas, por não causar problemas, é um uso legítimo.


3

É bom colocar um código que é executado em um ambiente relativamente complicado em um contêiner: eu o uso para criar uma ligação local para "window" e executar código destinado a um navegador da web.


3

Eu acho que o uso literal do objeto é interessante, como um substituto para usar um fechamento

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

ou a declaração with equivalente a um fechamento

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

Eu acho que o risco real é acidentalmente minipular variáveis ​​que não fazem parte da instrução with, e é por isso que eu gosto do objeto literal que está sendo passado, você pode ver exatamente o que ele será no contexto adicionado no código.


3

Eu criei uma função de "mesclagem" que elimina parte dessa ambiguidade com a withinstrução:

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 usá-lo de maneira semelhante with, mas sei que não afetará nenhum escopo que não pretendo que ele afete.

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

Para algumas partes curtas do código, eu gostaria de usar as funções trigonométricas como sin, cosetc. , no modo grau, em vez de no modo radiante. Para esse propósito, eu uso um AngularDegreeobjeto:

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

Então eu posso usar as funções trigonométricas no modo grau sem mais ruído de linguagem em um withbloco:

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

Isso significa: eu uso um objeto como uma coleção de funções, que eu habilito em uma região de código limitada para acesso direto. Eu acho isso útil.


não é uma boa idéia usar a withdeclaração dessa maneira, apenas dificulta a leitura do código, porque você não sabe qual função é global e qual função é chamada no escopo de with object, portanto, se alguma função não estiver definida no escopo do objeto, em seguida, ele vai tentar acessá-lo no espaço global
Saket Patel

Estar ciente do problema de escopo, ainda acho isso útil. Um matemático que lê o código quer ver diretamente que a fórmula acima é uma aplicação da "lei das 4 partes sucessivas" na trigonometria esférica. A alternativa estrita ofusca a fórmula: 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;daria o mesmo resultado, mas é o horror.
precisa saber é o seguinte

@rplantiko É importante lembrar que a maioria das pessoas não se sente confortável com isso. Portanto, a menos que você esteja escrevendo um código que ninguém mais irá tocar. Além disso, tenho certeza de que posso lhe mostrar alguns usos withque podem te surpreender.
Juan Mendes

2

Eu acho que a utilidade de withpode depender de quão bem o seu código é escrito. Por exemplo, se você estiver escrevendo um código que aparece assim:

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

então você pode argumentar que withmelhorará a legibilidade do código fazendo o seguinte:

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

Por outro lado, pode-se argumentar que você está violando a Lei de Demeter , mas, novamente, talvez não. Eu discordo =).

Acima de tudo, saiba que Douglas Crockford recomenda não usar with. Exorto-vos a verificar o seu blog sobre withe suas alternativas aqui .


Obrigado pela resposta, Tom. Eu li a recomendação de Crockford e, embora faça sentido, só vai tão longe. Estou pensando na idéia - tocada indiretamente por doekman - de que o verdadeiro poder do with {} está no modo como ele pode ser usado para manipular o escopo ...
Shog9

2

Eu realmente não vejo como usar o with é mais legível do que digitar object.member. Não acho que seja menos legível, mas também não.

Como lassevk disse, eu definitivamente posso ver como o uso com seria mais suscetível a erros do que apenas a sintaxe "object.member" muito explícita.


1

Você pode ver a validação de um formulário em javascript no W3schools http://www.w3schools.com/js/js_form_validation.asp onde o formulário do objeto é "varrido" para encontrar uma entrada com o nome 'email'

Mas eu o modifiquei para obter de QUALQUER formulário todos os campos validados como não vazios, independentemente do nome ou quantidade de campo em um formulário. Bem, eu testei apenas campos de texto.

Mas o with () tornou as coisas mais simples. Aqui está o código:

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

O fork do CoffeeScript do Coco tem uma withpalavra - chave, mas simplesmente define this(também gravável como @no CoffeeScript / Coco) o objeto de destino dentro do bloco. Isso remove a ambiguidade e atinge a conformidade com o modo estrito do ES5:

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

0

Aqui está um bom uso para with : adicionar novos elementos a um literal de objeto, com base nos valores armazenados nesse objeto. Aqui está um exemplo que eu acabei de usar hoje:

Eu tinha um conjunto de peças possíveis (com aberturas voltadas para cima, baixo, esquerda ou direita) que poderiam ser usadas, e eu queria uma maneira rápida de adicionar uma lista de peças que sempre seriam colocadas e trancadas no início do jogo . Eu não queria continuar digitando types.tbrpara cada tipo na lista, então apenas o usei 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

Você pode usar with para evitar a necessidade de gerenciar explicitamente o arity ao usar o require.js:

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

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

Implementação de requirejs.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

Como Andy E apontou nos comentários da resposta de Shog9, esse comportamento potencialmente inesperado ocorre ao usar withcom um objeto literal:

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]"
  }
}

Não que esse comportamento inesperado não fosse uma marca registrada with.

Se você realmente deseja usar essa técnica, use pelo menos um objeto com um protótipo nulo.

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"
  }
}

Mas isso só funcionará no ES5 +. Também não use with.


0

Estou trabalhando em um projeto que permitirá aos usuários fazer upload de código para modificar o comportamento de partes do aplicativo. Nesse cenário, eu tenho usado uma withcláusula para impedir que o código modifique qualquer coisa fora do escopo com o qual eu quero que eles mexam. A parte (simplificada) do código que eu uso para fazer isso é:

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

Esse código garante (um pouco) que o código definido pelo usuário não tenha acesso a objetos com escopo global, como windownem a nenhuma das minhas variáveis ​​locais, por meio de um fechamento.

Apenas como uma palavra para o sábio, ainda preciso executar verificações de código estático no código enviado pelo usuário para garantir que eles não estejam usando outras maneiras furtivas para acessar o escopo global. Por exemplo, o seguinte código definido pelo usuário obtém acesso direto a window:

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


0

Minhas

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
}

se resume a

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

Você pode confiar em códigos de baixa qualidade? Não, vemos que foi tornado absolutamente ilegível. Este exemplo prova inegavelmente que não há necessidade de declaração com, se eu estiver assumindo a legibilidade corretamente;)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.