variável === indefinido vs. tipo de variável === “indefinido”

300

As Diretrizes de estilo do jQuery Core sugerem duas maneiras diferentes de verificar se uma variável está definida.

  • Variáveis ​​globais: typeof variable === "undefined"
  • Variáveis ​​locais: variable === undefined
  • Propriedades: object.prop === undefined

Por que o jQuery usa uma abordagem para variáveis ​​globais e outra para locais e propriedades?

Patrick McElhaney
fonte
Não consigo responder à pergunta sobre por que o JQuery usaria as duas abordagens, mas o Javascript tem algumas peculiaridades interessantes que significam que essas duas coisas são sutilmente diferentes. Não deve importar a maior parte do tempo (ou seja, se o seu código é sensato), mas existem diferenças: veja aqui para uma redação - wtfjs.com/2010/02/15/undefined-is-mutable
Spudley
2
Como o @Struppi apontou, a função mais externa do jQuery possui um argumento chamado indefinido. No jQuery, foo === undefinedestá verificando a cópia local de indefinido em vez do global (window.undefined), que pode ter sido modificado por código insano. O fato de que indefinido é mutável é definitivamente digno de nota e estou feliz que você tenha feito. (+1)
Patrick McElhaney
1
O link atual para esse artigo é wtfjs.com/wtfs/2010-02-15-undefined-is-mutable
enigment

Respostas:

366

Para variáveis ​​não declaradas, typeof fooretornará a string literal "undefined", enquanto a verificação de identidade foo === undefinedacionaria o erro "foo não está definido" .

Para variáveis ​​locais (que você sabe que estão declaradas em algum lugar), esse erro não ocorreria, portanto, a verificação de identidade.

Linus Kleen
fonte
3
@goreSplatter Você não pode excluí-lo agora. :-) Foi difícil escolher, mas da maneira como a pergunta é formulada, essa resposta é mais adequada. Qualquer pessoa interessada em como o indefinido funciona em geral (como eu) também deve olhar para as outras respostas, especialmente as do Tim.
Patrick McElhaney
4
Eu adicionaria aspas ( typeof foo; // -> "undefined") para enfatizar que é uma string e não o valor primitivo undefined.
C24w 19/04
117

Eu continuaria usando em typeof foo === "undefined"todos os lugares. Isso nunca pode dar errado.

Eu imagino que a razão pela qual o jQuery recomenda os dois métodos diferentes é que eles definem sua própria undefinedvariável dentro da função em que o código jQuery reside, portanto, dentro dessa função, undefinedé seguro evitar adulterações externas. Eu também imaginaria que alguém em algum lugar comparou as duas abordagens diferentes e descobriu que foo === undefinedé mais rápido e, portanto, decidiu que é o caminho a seguir. [ATUALIZAÇÃO: como observado nos comentários, a comparação com undefinedtambém é um pouco mais curta, o que pode ser uma consideração.] No entanto, o ganho em situações práticas será totalmente insignificante: essa verificação nunca será algum tipo de gargalo e o que você perde é significativo: avaliar uma propriedade de um objeto host para comparação pode gerar um erro, enquanto umtypeof cheque nunca será.

Por exemplo, o seguinte é usado no IE para analisar XML:

var x = new ActiveXObject("Microsoft.XMLDOM");

Para verificar se ele possui um loadXMLmétodo com segurança:

typeof x.loadXML === "undefined"; // Returns false

Por outro lado:

x.loadXML === undefined; // Throws an error

ATUALIZAR

Outra vantagem da typeofverificação que eu esqueci de mencionar foi que ela também trabalha com variáveis ​​não declaradas, o que a foo === undefinedverificação não faz e, de fato, gera a ReferenceError. Obrigado a @LinusKleen por me lembrar. Por exemplo:

typeof someUndeclaredVariable; // "undefined"
someUndeclaredVariable === undefined; // throws a ReferenceError

Conclusão: sempre use o typeofcheque.

Tim Down
fonte
10
Obrigado Tim. Seu ponto de vista sobre desempenho faz sentido. A equipe do jQuery provavelmente está mais preocupada com o impacto no tamanho do arquivo. foo === undefined, quando minimizado, é provavelmente algo como f===u, enquanto que typeof foo === "undefined"só pode ser reduzido para typeof f==="undefined".
Patrick McElhaney
1
Você pode definir var u = "undefined"e reduzi-lo para typeof f==u, o que melhora as coisas, mas ainda é maior.
Tim baixo
5
Bons pontos, mas não tenho certeza de que a segurança typeofcontra variáveis ​​não declaradas seja uma vantagem. Se alguma coisa permitir, erros de digitação passam mais facilmente, e não consigo ver quando você realmente deseja verificar o tipo de variáveis ​​não declaradas.
David Tang
2
@ Box9: Eu posso imaginar usá-lo em uma biblioteca para verificar a presença de outra biblioteca.
Tim baixo
2
@ Jontro: Essa é uma razão para não usar JSLint então.
Tim Baixo
29

Ainda outro motivo para usar o tipo de variante: undefinedpode ser redefinido.

undefined = "foo";
var variable = "foo";
if (variable === undefined)
  console.log("eh, what?!");

O resultado de typeof variable não pode.

Atualização : observe que esse não é o caso no ES5, pois o global undefinedé uma propriedade não configurável e não gravável:

15.1.1 Propriedades do valor do objeto global
[...]
15.1.1.3 undefined
O valor de undefinedé indefinido (ver 8.1). Esta propriedade possui os atributos
{[[Gravável]]: false, [[Enumerable]]: false, [[Configurable]]: false}.

Mas ainda pode ser sombreado por uma variável local:

(function() {
  var undefined = "foo";
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})()

ou parâmetro:

(function(undefined) {
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})("foo")
Jakob
fonte
17
Não pode ser redefinido no ES5.
Ry-
6
A undefinedpropriedade global não pode ser redefinida no ES5, mas ainda pode ser sombreada com uma variável local. void 0é mais curto e seguro.
Oriol
7

Porque undefinednem sempre é declarado, mas o jQuery declara undefinedem sua função principal. Então, eles usam o undefinedvalor seguro internamente, mas, fora, usam o typeofestilo para serem seguros.

Struppi
fonte
1

Para variáveis ​​locais, a verificação com localVar === undefinedfuncionará porque eles devem ter sido definidos em algum lugar no escopo local ou não serão considerados locais.

Para variáveis ​​que não são locais e não estão definidas em nenhum lugar, a verificação someVar === undefinedlançará a exceção: Uncaught ReferenceError: j não está definido

Aqui está um código que esclarecerá o que estou dizendo acima. Por favor, preste atenção aos comentários embutidos para maior clareza .

function f (x) {
    if (x === undefined) console.log('x is undefined [x === undefined].');
    else console.log('x is not undefined [x === undefined.]');

    if (typeof(x) === 'undefined') console.log('x is undefined [typeof(x) === \'undefined\'].');
    else console.log('x is not undefined [typeof(x) === \'undefined\'].');

    // This will throw exception because what the hell is j? It is nowhere to be found.
    try
    {
        if (j === undefined) console.log('j is undefined [j === undefined].');
        else console.log('j is not undefined [j === undefined].');
    }
    catch(e){console.log('Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.');}

    // However this will not throw exception
    if (typeof j === 'undefined') console.log('j is undefined (typeof(x) === \'undefined\'). We can use this check even though j is nowhere to be found in our source code and it will not throw.');
    else console.log('j is not undefined [typeof(x) === \'undefined\'].');
};

Se chamarmos o código acima, assim:

f();

A saída seria esta:

x is undefined [x === undefined].
x is undefined [typeof(x) === 'undefined'].
Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.
j is undefined (typeof(x) === 'undefined'). We can use this check even though j is nowhere to be found in our source code and it will not throw.

Se chamarmos o código acima como este (com qualquer valor realmente):

f(null); 
f(1);

A saída será:

x is not undefined [x === undefined].
x is not undefined [typeof(x) === 'undefined'].
Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.
j is undefined (typeof(x) === 'undefined'). We can use this check even though j is nowhere to be found in our source code and it will not throw.

Quando você faz a verificação como esta:, typeof x === 'undefined'você está essencialmente perguntando isto: Verifique se a variávelx existe (foi definida) em algum lugar no código-fonte. (mais ou menos). Se você conhece C # ou Java, esse tipo de verificação nunca é feito porque, se não existir, não será compilado.

<== Fiddle Me ==>

CodificaçãoYoshi
fonte
1

Resumo:

Quando no escopo global, na verdade, queremos retornar true se a variável não for declarada ou tiver o valor undefined:

var globalVar1;

// This variable is declared, but not defined and thus has the value undefined
console.log(globalVar1 === undefined);

// This variable is not declared and thus will throw a referenceError
console.log(globalVar2 === undefined);

Como no escopo global, não temos 100% de certeza se uma variável é declarada, isso pode nos dar um referenceError. Quando usamos o typeofoperador na variável desconhecida, não estamos obtendo esse problema quando a variável não é declarada:

var globalVar1;

console.log(typeof globalVar1 === 'undefined');
console.log(typeof globalVar2 === 'undefined');

Isso se deve ao fato de o typeofoperador retornar a string undefinedquando uma variável não é declarada ou atualmente mantém o valor undefinedexatamente o que queremos.


  • Com variáveis ​​locais, não temos esse problema porque sabemos de antemão que essa variável existirá. Podemos simplesmente procurar na respectiva função se a variável estiver presente.
  • Com propriedades de objetos, não temos esse problema, porque quando tentamos procurar uma propriedade de objeto que não existe, também obtemos o valor undefined

var obj = {};

console.log(obj.myProp === undefined);

Willem van der Veen
fonte
-5

typeof a === 'undefined'é mais rápido do que a === 'undefined'em cerca de 2 vezes no nó v6.9.1.

Eduard Popov
fonte
3
Essas não são as mesmas coisas que você digitou. Eu acho que você quis dizer undefinedna segunda parte, não'undefined'
scaryguy