variable === indefinido versus tipo de variable === "indefinido"

300

Las Pautas de estilo de jQuery Core sugieren dos formas diferentes de verificar si una variable está definida.

  • Variables globales: typeof variable === "undefined"
  • Variables locales variable === undefined
  • Propiedades: object.prop === undefined

¿Por qué jQuery usa un enfoque para variables globales y otro para locales y propiedades?

Patrick McElhaney
fuente
No puedo responder la pregunta de por qué JQuery usaría ambos enfoques, pero Javascript tiene algunas peculiaridades interesantes que significan que estas dos cosas son sutilmente diferentes. No debería importar la mayor parte del tiempo (es decir, si su código es correcto), sin embargo, existen diferencias: consulte aquí para obtener un informe
wtfjs.com/2010/02/15/undefined-is-mutable
2
Como señaló @Struppi, la función más externa de jQuery tiene un argumento llamado undefined. Dentro de jQuery, foo === undefinedse compara con la copia local de undefined en lugar de la global (window.undefined), que puede haber sido modificada por un código loco. Vale la pena señalar el hecho de que indefinido es mutable y me alegro de que lo haya hecho. (+1)
Patrick McElhaney el
1
El enlace actual para ese artículo es wtfjs.com/wtfs/2010-02-15-undefined-is-mutable
enigment el

Respuestas:

366

Para las variables no declaradas, typeof foodevolverá el literal de cadena "undefined", mientras que la verificación de identidad foo === undefinedactivaría el error "foo no está definido" .

Para las variables locales (que sabe que se declaran en alguna parte), no se produciría dicho error, de ahí la verificación de identidad.

Linus Kleen
fuente
3
@goreSplatter No puede eliminarlo ahora. :-) Fue difícil elegir, pero la forma en que se formula la pregunta, esta respuesta es mejor. Cualquiera que esté interesado en cómo funciona lo indefinido en general (como yo) también debería mirar las otras respuestas, especialmente las de @ Tim.
Patrick McElhaney
44
Agregaría comillas ( typeof foo; // -> "undefined") para enfatizar que es una cadena y no el valor primitivo undefined.
c24w
117

Me apegaría a usar en typeof foo === "undefined"todas partes. Eso nunca puede salir mal.

Me imagino que la razón por la cual jQuery recomienda los dos métodos diferentes es que definen su propia undefinedvariable dentro de la función en la que vive el código jQuery, por lo que dentro de esa función undefinedestá a salvo de manipulaciones externas. También me imagino que alguien en algún lugar ha comparado los dos enfoques diferentes y descubierto que foo === undefinedes más rápido y, por lo tanto, decidió que es el camino a seguir. [ACTUALIZACIÓN: como se señaló en los comentarios, la comparación con undefinedes también un poco más corta, lo que podría ser una consideración.] Sin embargo, la ganancia en situaciones prácticas será completamente insignificante: esta verificación nunca será un tipo de cuello de botella, y qué perder es importante: evaluar una propiedad de un objeto host para comparar puede arrojar un error mientras que untypeof el cheque nunca lo hará.

Por ejemplo, lo siguiente se usa en IE para analizar XML:

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

Para verificar si tiene un loadXMLmétodo de forma segura:

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

Por otra parte:

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

ACTUALIZAR

Otra ventaja de la typeofverificación que olvidé mencionar es que también funciona con variables no declaradas, que la foo === undefinedverificación no hace, y de hecho arroja un ReferenceError. Gracias a @LinusKleen por recordármelo. Por ejemplo:

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

En pocas palabras: siempre use el typeofcheque.

Tim Down
fuente
10
Gracias Tim. Su punto sobre el rendimiento tiene sentido. El equipo de jQuery probablemente esté más preocupado por el impacto en el tamaño del archivo. foo === undefined, cuando se minimiza, es probablemente algo así f===u, mientras typeof foo === "undefined"que solo se puede reducir a typeof f==="undefined".
Patrick McElhaney
1
Podría definirlo var u = "undefined"y reducirlo a typeof f==u, lo que mejora las cosas pero aún es más grande.
Tim Down
55
Buenos puntos, pero no estoy seguro de que la seguridad typeoffrente a variables no declaradas sea una ventaja. En todo caso, permite que los errores tipográficos pasen más fácilmente, y no puedo ver cuándo realmente querría verificar el tipo de variables no declaradas.
David Tang
2
@ Recuadro 9: Me imagino usarlo en una biblioteca para verificar la presencia de otra biblioteca.
Tim Down
2
@ jontro: Esa es una razón para no usar JSLint entonces.
Tim Down
29

Otra razón más para usar el tipo de variante: undefinedse puede redefinir.

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

El resultado de typeof variable no se puede.

Actualización : tenga en cuenta que este no es el caso en ES5, donde el global undefinedes una propiedad no configurable, no escribible:

15.1.1 Propiedades de valor del objeto global
[...]
15.1.1.3 indefinido
El valor de undefinedes indefinido (véase 8.1). Esta propiedad tiene los atributos
{[[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}.

Pero aún puede ser sombreado por una variable local:

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

o parámetro:

(function(undefined) {
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})("foo")
Jakob
fuente
17
No se puede redefinir en ES5.
Ry-
66
La undefinedpropiedad global no se puede redefinir en ES5, pero aún se puede sombrear con una variable local. void 0Es más corto y seguro.
Oriol
7

Porque undefinedno siempre se declara, pero jQuery declara undefineden su función principal. Entonces usan el undefinedvalor seguro internamente, pero afuera, usan el typeofestilo para estar seguros.

Struppi
fuente
1

Para las variables locales, verificar con localVar === undefinedfuncionará porque deben haberse definido en algún lugar dentro del alcance local o no se considerarán locales.

Para las variables que no son locales y no están definidas en ninguna parte, la comprobación someVar === undefinedarrojará una excepción: Error de referencia no capturado: j no está definido

Aquí hay un código que aclarará lo que digo anteriormente. Por favor, preste atención a los comentarios en línea para mayor claridad .

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

Si llamamos al código anterior así:

f();

El resultado sería este:

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.

Si llamamos al código anterior como estos (con cualquier valor en realidad):

f(null); 
f(1);

La salida 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.

Cuando realiza la comprobación de esta manera: typeof x === 'undefined'esencialmente pregunta esto: compruebe si la variable xexiste (se ha definido) en algún lugar del código fuente. (más o menos). Si conoce C # o Java, este tipo de verificación nunca se realiza porque si no existe, no se compilará.

<== Fiddle Me ==>

CodificaciónYoshi
fuente
1

Resumen:

Cuando en el ámbito global realmente queremos devolver verdadero si la variable no se declara o tiene el 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);

Debido a que en el ámbito global no estamos 100% seguros de si se declara una variable, esto podría darnos un error de referencia. Cuando usamos el typeofoperador en la variable desconocida, no tenemos este problema cuando la variable no se declara:

var globalVar1;

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

Esto se debe al hecho de que el typeofoperador devuelve la cadena undefinedcuando una variable no se declara o actualmente contiene el valor undefinedque es exactamente lo que queremos.


  • Con las variables locales no tenemos este problema porque sabemos de antemano que esta variable existirá. Simplemente podemos mirar en la función respectiva si la variable está presente.
  • Con las propiedades de objeto no tenemos este problema porque cuando intentamos buscar una propiedad de objeto que no existe también obtenemos el valor undefined

var obj = {};

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

Willem van der Veen
fuente
-5

typeof a === 'undefined'es más rápido que a === 'undefined'aproximadamente 2 veces en el nodo v6.9.1.

Eduard Popov
fuente
3
Esas no son las mismas cosas que escribiste. Creo que te referías undefineda la segunda parte, no'undefined'
scaryguy