zmienna === niezdefiniowana vs. typeof zmienna === „niezdefiniowana”

300

W jQuery podstawowe wytyczne Style wskazują na dwa różne sposoby, aby sprawdzić, czy zmienna jest zdefiniowana.

  • Zmienne globalne: typeof variable === "undefined"
  • Zmienne lokalne: variable === undefined
  • Nieruchomości: object.prop === undefined

Dlaczego jQuery używa jednego podejścia do zmiennych globalnych, a drugiego do zmiennych lokalnych i właściwości?

Patrick McElhaney
źródło
Nie potrafię odpowiedzieć na pytanie, dlaczego JQuery użyłby obu podejść, ale JavaScript ma kilka interesujących dziwactw, które oznaczają, że te dwie rzeczy są nieco inne. Przez większość czasu nie powinno to mieć znaczenia (np. Czy twój kod jest rozsądny), ale są jednak różnice: zajrzyj
Spudley
2
Jak wskazał @Struppi, najbardziej zewnętrzna funkcja jQuery ma argument o nazwie niezdefiniowany. W jQuery foo === undefinedsprawdza lokalną kopię niezdefiniowanej zamiast globalnej (window.undefined), która mogła zostać zmodyfikowana przez szalony kod. Fakt, że niezdefiniowany jest zmienny, jest zdecydowanie wart uwagi i cieszę się, że tak. (+1)
Patrick McElhaney
1
Aktualny link do tego artykułu to wtfjs.com/wtfs/2010-02-15-undefined-is-mutable
rejestracja

Odpowiedzi:

366

W przypadku niezadeklarowanych zmiennych typeof foozwróci literał ciągu "undefined", natomiast sprawdzenie tożsamości foo === undefinedspowoduje błąd „nie zdefiniowano foo” .

Dla zmiennych lokalnych (które znają deklarowane są gdzieś), nie ma takiego błędu mogą się pojawić, stąd sprawdzenie tożsamości.

Linus Kleen
źródło
3
@goreSplatter Nie możesz go teraz usunąć. :-) Trudno było wybrać, ale sposób, w jaki pytanie jest sformułowane, ta odpowiedź jest lepsza. Każdy, kto jest zainteresowany tym, jak ogólnie nieokreślone działa (tak jak ja), powinien również spojrzeć na inne odpowiedzi, szczególnie @ Tima.
Patrick McElhaney
4
Dodałbym cudzysłowy ( typeof foo; // -> "undefined"), aby podkreślić, że jest to ciąg znaków, a nie pierwotna wartość undefined.
c24w 19.04.13
117

Chciałbym używać typeof foo === "undefined"wszędzie. To nigdy nie może pójść źle.

Wyobrażam sobie, że powodem, dla którego jQuery zaleca dwie różne metody, jest to, że definiują one swoją własną undefinedzmienną w funkcji, w której żyje kod jQuery, więc wewnątrz tej funkcji undefinedmożna bezpiecznie manipulować z zewnątrz. Wyobrażam sobie również, że ktoś gdzieś porównał dwa różne podejścia i odkrył, że foo === undefinedjest to szybsze, i dlatego postanowił, że tak należy. [AKTUALIZACJA: jak zauważono w komentarzach, porównanie z undefinedjest również nieco krótsze, co może być rozważeniem.] Jednak zysk w praktycznych sytuacjach będzie zupełnie nieistotny: ta kontrola nigdy nie będzie nigdy żadnym wąskim gardłem i co utrata jest znacząca: ocena właściwości obiektu hosta do porównania może spowodować błąd, podczas gdy atypeof sprawdź nigdy nie będzie.

Na przykład w IE do analizowania XML używane są:

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

Aby sprawdzić, czy loadXMLmetoda ma bezpiecznie:

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

Z drugiej strony:

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

AKTUALIZACJA

Kolejną zaletą typeofczeku, o którym zapomniałem wspomnieć, jest to, że działa on również z niezadeklarowanymi zmiennymi, czego foo === undefinednie sprawdza i faktycznie rzuca ReferenceError. Dzięki @LinusKleen za przypomnienie. Na przykład:

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

Konkluzja: zawsze używaj typeofczeku.

Tim Down
źródło
10
Dzięki Tim. Twój punkt widzenia na temat wydajności ma sens. Zespół jQuery jest prawdopodobnie bardziej zaniepokojony wpływem na rozmiar pliku. foo === undefined, po zminimalizowaniu, jest prawdopodobnie czymś podobnym f===u, podczas gdy typeof foo === "undefined"można go jedynie zredukować do typeof f==="undefined".
Patrick McElhaney,
1
Możesz go zdefiniować var u = "undefined"i zredukować do typeof f==u, co poprawia sytuację, ale wciąż jest większe.
Tim Down
5
Dobre punkty, ale nie jestem pewien, czy bezpieczeństwo typeofprzed niezadeklarowanymi zmiennymi jest zaletą. Jeśli cokolwiek, pozwala to na łatwiejsze pomijanie literówek i nie widzę, kiedy naprawdę chcesz sprawdzić typ niezadeklarowanych zmiennych.
David Tang,
2
@ Box9: Mogę sobie wyobrazić używanie go w bibliotece do sprawdzania obecności innej biblioteki.
Tim Down
2
@jontro: To jest jeden powód, aby nie używać JSLint.
Tim Down,
29

Jeszcze jeden powód korzystania z wariantu typeof: undefinedmożna zdefiniować na nowo.

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

Wynik typeof variable nie może.

Aktualizacja : pamiętaj, że nie ma to miejsca w ES5, ponieważ globalny undefinedjest właściwością, której nie można konfigurować i nie można zapisywać:

15.1.1 Właściwości wartości obiektu globalnego
[...]
15.1.1.3 undefined
Wartość parametru undefinedjest niezdefiniowana (patrz 8.1). Ta właściwość ma atrybuty
{[[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}.

Ale nadal może być przesłaniany przez zmienną lokalną:

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

lub parametr:

(function(undefined) {
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})("foo")
Jakob
źródło
17
Nie można przedefiniować w ES5.
Ry-
6
Globalnej undefinedwłaściwości nie można przedefiniować w ES5, ale nadal można ją zacieniać zmienną lokalną. void 0jest krótszy i bezpieczniejszy.
Oriol
7

Ponieważ undefinednie zawsze jest deklarowane, ale jQuery deklaruje undefinedw swojej głównej funkcji. Dlatego używają bezpiecznej undefinedwartości wewnętrznie, ale na zewnątrz używają typeofstylu, aby być bezpiecznym.

Struppi
źródło
1

W przypadku zmiennych lokalnych sprawdzanie za pomocą localVar === undefinedbędzie działać, ponieważ muszą zostać zdefiniowane gdzieś w zasięgu lokalnym lub nie będą uważane za lokalne.

W przypadku zmiennych, które nie są lokalne i nie są nigdzie zdefiniowane, sprawdzanie someVar === undefinedspowoduje zgłoszenie wyjątku: Uncaught ReferenceError: j nie jest zdefiniowany

Oto kod, który wyjaśni to, co mówię powyżej. Proszę zwrócić uwagę na wbudowane komentarze dla dalszej przejrzystości .

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

Jeśli wywołamy powyższy kod w ten sposób:

f();

Wynik byłby następujący:

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.

Jeśli wywołamy powyższy kod w ten sposób (z dowolną wartością):

f(null); 
f(1);

Dane wyjściowe będą:

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.

Kiedy wykonujesz sprawdzenie w ten sposób: typeof x === 'undefined'zasadniczo pytasz: Sprawdź, czy zmienna xistnieje (została zdefiniowana) gdzieś w kodzie źródłowym. (mniej więcej). Jeśli znasz język C # lub Java, ten typ sprawdzania nigdy nie jest wykonywany, ponieważ jeśli nie istnieje, nie zostanie skompilowany.

<== Fiddle Me ==>

Kodowanie Yoshi
źródło
1

Podsumowanie:

Gdy w zakresie globalnym rzeczywiście chcemy zwrócić wartość true, jeśli zmienna nie jest zadeklarowana lub ma wartość 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);

Ponieważ w zakresie globalnym nie jesteśmy w 100% pewni, że deklaracja zmiennej może dać nam błąd referencyjny. Kiedy używamy typeofoperatora nieznanej zmiennej, nie pojawia się ten problem, gdy zmienna nie jest zadeklarowana:

var globalVar1;

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

Wynika to z faktu, że typeofoperator zwraca łańcuch, undefinedgdy zmienna nie jest zadeklarowana lub aktualnie przechowuje wartość undefineddokładnie taką, jakiej chcemy.


  • W przypadku zmiennych lokalnych nie mamy tego problemu, ponieważ wcześniej wiemy, że ta zmienna będzie istnieć. Możemy po prostu sprawdzić odpowiednią funkcję, jeśli zmienna jest obecna.
  • W przypadku właściwości obiektu nie mamy tego problemu, ponieważ gdy próbujemy wyszukać właściwość obiektu, która nie istnieje, otrzymujemy również wartość undefined

var obj = {};

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

Willem van der Veen
źródło
-5

typeof a === 'undefined'jest szybszy niż a === 'undefined'około 2 razy w węźle v6.9.1.

Eduard Popow
źródło
3
To nie są te same rzeczy, które wpisałeś. Myślę, że miałeś na myśli undefineddrugą część, a nie'undefined'
przerażające