O quão perigoso é em JavaScript, realmente, assumir que undefined não é sobrescrito?

86

Sempre que alguém menciona testar contra undefined, é indicado que undefinednão é uma palavra-chave para que pudesse ser definida como"hello" , portanto, você deve usar typeof x == "undefined" . Isso me parece ridículo. Ninguém jamais faria isso, e se fizesse, seria motivo suficiente para nunca usar qualquer código que eles escreveram ... certo?

Eu encontrei um exemplo de alguém que acidentalmente definiu undefinedcomo null, e isso foi dado como um motivo para evitar supor que undefinednão seja sobrescrito. Mas se eles tivessem feito isso, o bug não teria sido detectado, e não consigo ver como é melhor.

Em C ++, todos sabem que é legal dizer isso #define true false, mas ninguém nunca aconselha que você evite truee use 0 == 0. Você simplesmente assume que ninguém jamais seria um idiota grande o suficiente para fazer isso e, se o fizer, nunca confie em seu código novamente.

Isso alguma vez realmente mordeu alguém onde outra pessoa foi atribuída undefined(propositalmente) e quebrou seu código, ou é mais uma ameaça hipotética? Estou disposto a arriscar para tornar meu código um pouco mais legível. É realmente uma má ideia?

Para reiterar, estou não pedir como se proteger contra redeterminação indefinido. Já vi esses truques escritos 100 vezes. Estou perguntando o quão perigoso é não usar esses truques.

Cosmologicon
fonte
7
Eu ficaria feliz, mas não acho que nenhuma das três respostas dadas faça um bom trabalho ao responder à pergunta. Tentei deixar claro que não estava pedindo uma repetição das respostas que vinculei, mas ainda assim foi o que consegui. Se for considerado estúpido não aceitar uma resposta, mesmo que não seja o que eu estava perguntando, me avise e irei aceitar!
Cosmologicon
1
Acho que todos os fatos estão expostos aqui, no entanto. Portanto, o que você está dizendo é que, em vez disso, deseja uma resposta ou opinião subjetiva, o que só leva à discordância. É a razão pela qual a frase "prática recomendada" não é permitida nos títulos das perguntas. Você é a única pessoa que pode saber como isso é perigoso em seu cenário. E se você está escrevendo uma biblioteca popular na qual não controla todas as 'variáveis', o wrapper de função (indefinido) {} parece ser uma excelente resposta. Por que não usá-lo e aceitar essa resposta?
Shannon
4
Se alguém está preocupado com a redefinição de alguém undefined, você deve se preocupar mais com a redefinição de alguém XMLHttpRequest, ou alert. Todas as funções que usamos windowpoderiam ter sido redefinidas. E se você está apenas preocupado com um colega de trabalho fazendo isso por acidente, por que confiar que ele não fará window.addEventListener = "coolbeans"? A resposta é não se preocupar com nada disso. Se alguém está injetando JS maliciosamente em sua página, você está bloqueado de qualquer maneira. Trabalhe para evitar que isso aconteça em primeiro lugar.
Chris Middleton

Respostas:

56

Não, eu nunca fiz. Isso ocorre principalmente porque eu desenvolvo em navegadores modernos, que são em sua maioria compatíveis com ECMAScript 5. O padrão ES5 determina que undefinedagora é somente leitura. Se você usar o modo estrito (deveria), um erro será gerado se você tentar modificá-lo acidentalmente.

undefined = 5;
alert(undefined); // still undefined
'use strict';
undefined = 5; // throws TypeError

O que você não deve fazer é criar seu próprio escopo, mutável undefined:

(function (undefined) {
    // don't do this, because now `undefined` can be changed
    undefined = 5;
})();

Constante está bem. Ainda desnecessário, mas ótimo.

(function () {
    const undefined = void 0;
})();
Ry-
fonte
Sua primeira declaração apenas garante que você não sobrescreva acidentalmente undefined durante o desenvolvimento; ela ainda representa a mesma ameaça ao ser executada com outro código em computadores cliente.
bennedich
1
@bennedich: Eu sei, eu estava dizendo que nunca tive esse problema, mas aqui está o que você deve fazer .
Ry-
1
@bennedich Você não tem a mesma chance de sobrescrever acidentalmente qualquer outra variável?
Ates Goral
40

Nenhum código adequado fará tal coisa. Mas você nunca pode saber o que algum desenvolvedor aspirante a esperto ou um plugin / biblioteca / script que você está usando fez. Por outro lado, é extremamente improvável e os navegadores modernos não permitirão a sobregravação de forma undefinedalguma, portanto, se você estiver usando esse navegador para desenvolvimento, notará rapidamente se algum código tentar sobrescrevê-lo.


E mesmo que você não tenha perguntado por isso, muitas pessoas provavelmente encontrarão essa pergunta ao procurarem o problema mais comum "como se proteger contra redefinições undefined", então responderei de qualquer maneira:

Há uma maneira muito boa de obter um indefinido, undefined independentemente da idade do navegador:

(function(undefined) {
    // your code where undefined is undefined
})();

Isso funciona porque um argumento que não é especificado é sempre undefined. Você também pode fazer isso com uma função que aceita alguns argumentos reais, por exemplo, como este quando você está usando jQuery. Geralmente é uma boa ideia garantir um ambiente são desta forma:

(function($, window, undefined) {
    // your code where undefined is undefined
})(jQuery, this);

Então você pode ter certeza de que dentro dessa função anônima, as seguintes coisas são verdadeiras:

  • $ === jQuery
  • window === [the global object]
  • undefined === [undefined].

No entanto, observe que às vezes typeof x === 'undefined'é realmente necessário: Se a variável xnunca foi definida com um valor (ao contrário de ter sido definida como undefined), a leitura xde uma maneira diferente, que if(x === undefined)gerará um erro. Isso não se aplica às propriedades do objeto, portanto, se você sabe que yé sempre um objeto, if(y.x === undefined)é perfeitamente seguro.

ThiefMaster
fonte
2
Sua afirmação sobre xnão ter um valor definido não é totalmente verdadeira (consulte jsfiddle.net/MgADz ); é, sim, se realmente não estiver definido (consulte jsfiddle.net/MgADz/1 ).
Ry-
7
Novamente, se alguém acidentalmente disser undefined = someVariableque é um bug e você quiser que as coisas quebrem. Pelo menos eu quero.
Cosmologicon
2
Eu trabalhei com uma base de código que tinha um script legado reduzido que sobrescreveu undefined, não tínhamos tempo ou orçamento para reescrever o script, este é um exemplo em que typeof x == "undefined" foi necessário, não é tão estranho e provavelmente um bom hábito.
Damen TheSifter
2
Estou interessado em saber por que todos os exemplos usam "indefinido" como argumento adicional na função? Mas pode haver uma situação se alguém passar um parâmetro extra (definido) por erro e toda a lógica falhar. Por que não usar algo como function () {var _undef; if (someVal == _undef) {faça ​​algo}};
Oleksandr_DJ
4
@Oleksandr_DJ Exatamente. Invente um valor que você sabe que é undefinede atribua-o ao undefinedescopo. Não deixe undefinedaberto para sobrescrever se você passar "muitos" argumentos. Isso convida a erros e é um padrão inerentemente não defensivo, especialmente quando, como Lucero aponta , existem alternativas muito fáceis para obter undefinedvalores sólidos no escopo.
ruffin de
19

Existe uma solução simples para isso: compare com o void 0que é sempre indefinido.

Observe que você deve evitar ==, pois pode coagir os valores. Use ===(e !==) em vez disso.

Dito isso, a variável indefinida pode ser definida por erro se alguém escrever em =vez de ==ao comparar algo com undefined.

Lucero
fonte
IMO, esta é a melhor resposta.
abhisekp
1
Considere que a foo === void 0leitura pode não ser tão suave quanto foo === undefinede que imutável undefinedé totalmente compatível com os navegadores modernos (IE 9+), como você pode ver nesta tabela de compatibilidade.
Daniel AR Werner
3

Só você sabe que código usa e, portanto, o quão perigoso é. Esta pergunta não pode ser respondida da maneira que você esclareceu que gostaria que fosse respondida.

1) Crie uma política de equipe, não permita a redefinição de undefined, reservando-a para seu uso mais popular. Escaneie seu código existente para atribuição esquerda indefinida.

2) Se você não controlar todos os cenários, se seu código for usado fora das situações que você ou suas políticas controlam, obviamente sua resposta será diferente. Digitalize o código que usa seus scripts. Caramba, procure na web por estatísticas de atribuição esquerda indefinida, se desejar, mas eu duvido que isso tenha sido feito para você, porque é mais fácil apenas seguir a resposta # 1 ou # 3 aqui.

3) E se essa resposta não for boa o suficiente, provavelmente é porque, novamente, você precisa de uma resposta diferente. Talvez você esteja escrevendo uma biblioteca popular que será usada dentro de firewalls corporativos e não tenha acesso ao código de chamada. Em seguida, use uma das outras boas respostas aqui. Observe que a popular biblioteca jQuery pratica o encapsulamento de som e começa:

(function( window, undefined ) {

Só você pode responder à sua pergunta da maneira específica que procura. O que mais há pra dizer?

edit: ps se você realmente quer minha opinião, eu direi que não é perigoso. Qualquer coisa que possa causar defeitos (como atribuir a indefinidos, o que é obviamente um comportamento de risco bem documentado) é em si um defeito. É o defeito que está o risco. Mas isso é apenas em meus cenários, onde posso manter essa perspectiva. Como recomendo que você faça, respondi à pergunta para meus casos de uso.

Shannon
fonte
3

É seguro testar contra indefinidos. Como você já mencionou. Se você obtiver algum código que o substitua (o que é altamente improvável), simplesmente não o use mais.

Talvez se você estiver criando uma biblioteca para uso público, você pode usar algumas das técnicas para evitar que o usuário a altere. Mas, mesmo neste caso, o problema é deles, não sua biblioteca.

Bart Calixto
fonte
2

Você pode usar undefinedem seu código ao codificar para navegadores que suportam ECMAScript 5.1, pois é imutável de acordo com a especificação do idioma .

Consulte também esta tabela de compatibilidade ou este caniuse ECMAScript 5 para ver se todos os navegadores modernos (IE 9+) foram implementados imutáveisundefined .

Daniel AR Werner
fonte
1

Não é nada perigoso. Ele só pode ser sobrescrito quando executado em um motor ES3 e não é provável que seja usado mais.

kirk.burleson
fonte
0

Em primeiro lugar, se o seu código quebra, provavelmente não é porque algum outro desenvolvedor por aí "está tentando ser um idiota", como você disse.

É verdade que undefinednão é uma palavra-chave. Mas é um primitivo de nível global. Ele foi planejado para ser usado assim (consulte "undefined" em developer.mozilla.org ):

var x;
if (x === undefined) {
    // these statements execute
}
else {
    // these statements do not execute
}

A alternativa comum para isso (também do MDN ) e na minha opinião a melhor maneira é:

// x has not been declared before
if (typeof x === 'undefined') { // evaluates to true without errors
    // these statements execute
}

if(x === undefined){ // throws a ReferenceError

}

O que tem algumas vantagens, a óbvia (a partir dos comentários) é que não dispara uma exceção quando x não é declarado. Também é importante notar que MDN também aponta que é importante usar ===over ==no primeiro caso porque:

var x=null;
if (x === undefined) {
    // this is probably what you meant to do
    // these lines will not execute in this case
}
else if (x == undefined) {
    // these statements will execute even though x *is* defined (as null)
}
else {
    // these statements do not execute
}

Esse é outro motivo frequentemente esquecido pelo qual provavelmente é melhor usar apenas a segunda alternativa em todos os casos.

Conclusão: não é errado codificar da primeira forma e certamente não é perigoso. O argumento que você viu e que usa como exemplo contra ele (que pode ser sobrescrito) não é o argumento mais forte para codificar a alternativa typeof. Mas o uso typeofé mais forte por um motivo específico: ele não lança uma exceção quando seu var não é declarado. Também pode-se argumentar que usar em ==vez de ===é um erro comum, caso em que não está fazendo o que você esperava. Então, por que não usar typeof?

Polvo
fonte
1
"O que tem algumas vantagens, a óbvia é que não dispara uma exceção quando x não é declarado." Deixe-me ver se entendi. Você acha que é uma vantagem ignorar silenciosamente a exceção que acontece quando você usa uma variável não declarada? Isso parece muito sujeito a erros. Você pode explicar por que acha que isso não é uma desvantagem terrível?
Cosmologicon