переменная === неопределенная или переменная typeof === «неопределенная»

300

В Рекомендации Jquery Основные Стиль предлагают два различных способа проверить, определена ли переменная.

  • Глобальные переменные: typeof variable === "undefined"
  • Локальные переменные: variable === undefined
  • Свойства: object.prop === undefined

Почему jQuery использует один подход для глобальных переменных, а другой - для локальных переменных и свойств?

Патрик МакЭлхани
источник
Я не могу ответить на вопрос о том, почему JQuery будет использовать оба подхода, но у Javascript есть некоторые интересные особенности, которые означают, что эти две вещи слегка различаются. Это не должно иметь значения большую часть времени (то есть, если ваш код вменяемый), но, тем не менее, есть различия: см. Здесь статью
wtfjs.com/2010/02/15/undefined-is-mutable
2
Как указал @Struppi, внешняя функция jQuery имеет аргумент с именем undefined. В jQuery foo === undefinedпроверяет локальную копию undefined вместо глобальной (window.undefined), которая могла быть изменена с помощью безумного кода. Тот факт, что undefined является изменяемым, определенно стоит отметить, и я рад, что вы это сделали. (+1)
Патрик МакЭлхани
1
Текущая ссылка на эту статью: wtfjs.com/wtfs/2010-02-15-undefined-is-mutable
enigment

Ответы:

366

Для необъявленных переменных typeof fooвернет строковый литерал "undefined", тогда как проверка идентичности foo === undefinedвызовет ошибку «foo не определен» .

Для локальных переменных (которые, как вы знаете , где-то объявлены) такая ошибка не возникнет, следовательно, проверка идентичности.

Линус Клин
источник
3
@goreSplatter Вы не можете удалить его сейчас. :-) Сложно было выбрать, но, так как вопрос сформулирован, этот ответ лучше подходит. Любой, кто интересуется тем, как вообще работает undefined (как и я), должен также посмотреть другие ответы, особенно @Tim.
Патрик МакЭлхани
4
Я хотел бы добавить кавычки ( typeof foo; // -> "undefined"), чтобы подчеркнуть, что это строка, а не примитивное значение undefined.
c24w
117

Я бы использовал typeof foo === "undefined"везде. Это никогда не может пойти не так.

Я предполагаю, что причина, по которой jQuery рекомендует два разных метода, заключается в том, что они определяют свою собственную undefinedпеременную в функции, в которой находится код jQuery, поэтому внутри этой функции undefinedона защищена от вмешательства извне. Я также предположил бы, что кто-то где-то сравнил два разных подхода и обнаружил, что foo === undefinedэто быстрее, и поэтому решил, что это путь. [ОБНОВЛЕНИЕ: как отмечено в комментариях, сравнение с undefinedтакже немного короче, что может быть соображением.] Однако, выигрыш в практических ситуациях будет крайне незначительным: эта проверка никогда не будет узким местом, и что Вы теряете значение: оценка свойства хост-объекта для сравнения может вызвать ошибку, тогда какtypeof проверять никогда не буду.

Например, следующее используется в IE для анализа XML:

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

Чтобы проверить, есть ли у него loadXMLметод безопасно:

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

С другой стороны:

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

ОБНОВИТЬ

Еще одно преимущество typeofпроверки, которую я забыл упомянуть, заключалось в том, что она также работает с необъявленными переменными, чего нет в foo === undefinedпроверке, и фактически выдает a ReferenceError. Спасибо @LinusKleen за напоминание. Например:

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

Итог: всегда используйте typeofчек.

Тим Даун
источник
10
Спасибо, Тим. Ваша точка зрения о производительности имеет смысл. Команда jQuery, вероятно, больше обеспокоена влиянием на размер файла. foo === undefinedпри минимизации, вероятно, что-то вроде f===u, тогда как typeof foo === "undefined"может быть уменьшено только до typeof f==="undefined".
Патрик МакЭлхани
1
Вы можете определить var u = "undefined"и уменьшить его typeof f==u, что улучшает вещи, но все еще больше.
Тим Даун
5
Хорошие моменты, но я не уверен, что безопасность от typeofнеобъявленных переменных является преимуществом. Если что-то и позволяет опечаткам ускользнуть, я не могу понять, когда вы действительно захотите проверить тип необъявленных переменных.
Дэвид Тан
2
@ Box9: я могу себе представить, используя его в библиотеке, чтобы проверить наличие другой библиотеки.
Тим Даун
2
@jontro: Это одна из причин не использовать JSLint тогда.
Тим Даун
29

Еще одна причина использования typeof-варианта: undefinedможет быть переопределена.

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

Результат typeof variable не может.

Обновление : обратите внимание, что в ES5 дело обстоит иначе: глобальное свойство undefinedявляется неконфигурируемым и не записываемым свойством:

15.1.1 Свойства значения глобального объекта
[...]
15.1.1.3 undefined
Значение undefinedне определено (см. 8.1). Это свойство имеет атрибуты
{[[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}.

Но он все еще может быть скрыт локальной переменной:

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

или параметр:

(function(undefined) {
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})("foo")
Jakob
источник
17
Не может быть переопределено в ES5.
Ry-
6
Глобальное undefinedсвойство не может быть переопределено в ES5, но все еще может быть затенено локальной переменной. void 0короче и безопаснее.
Oriol
7

Потому undefinedчто не всегда объявляется, но jQuery объявляет undefinedв своей основной функции. Таким образом, они используют безопасное undefinedзначение внутри, но снаружи они используют typeofстиль, чтобы быть безопасными.

Struppi
источник
1

Для локальных переменных проверка с помощью localVar === undefinedбудет работать, потому что они должны быть определены где-то в локальной области видимости, или они не будут считаться локальными.

Для переменных, которые не являются локальными и нигде не определены, проверка someVar === undefinedвызовет исключение: Uncaught ReferenceError: j не определено

Вот код, который прояснит то, что я говорю выше. Пожалуйста, обратите внимание на встроенные комментарии для дальнейшей ясности .

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

Если мы назовем приведенный выше код так:

f();

Выход будет такой:

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.

Если мы вызываем приведенный выше код следующим образом (с любым значением на самом деле):

f(null); 
f(1);

Выход будет:

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.

Когда вы делаете проверку следующим образом: typeof x === 'undefined'вы, по сути, спрашиваете следующее: Пожалуйста, проверьте, существует ли переменная x(была определена) где-то в исходном коде. (более или менее). Если вы знаете C # или Java, этот тип проверки никогда не выполняется, потому что если он не существует, он не будет компилироваться.

<== Fiddle Me ==>

CodingYoshi
источник
1

Резюме:

Когда в глобальной области мы на самом деле хотим вернуть true, если переменная не объявлена ​​или имеет значение 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);

Потому что в глобальной области мы не уверены на 100%, если объявлена ​​переменная, это может дать нам referenceError. Когда мы используем typeofоператор для неизвестной переменной, мы не получаем эту проблему, когда переменная не объявлена:

var globalVar1;

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

Это связано с тем, что typeofоператор возвращает строку, undefinedкогда переменная не объявлена ​​или в настоящее время содержит значение, undefinedкоторое является именно тем, что мы хотим.


  • С локальными переменными у нас нет этой проблемы, потому что мы заранее знаем, что эта переменная будет существовать. Мы можем просто посмотреть в соответствующей функции, если переменная присутствует.
  • Со свойствами объекта у нас нет этой проблемы, потому что, когда мы пытаемся найти свойство объекта, которое не существует, мы также получаем значение undefined

var obj = {};

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

Виллем ван дер Веен
источник
-5

typeof a === 'undefined'быстрее, чем a === 'undefined'примерно в 2 раза на узле v6.9.1.

Эдуард Попов
источник
3
Это не те вещи, которые вы напечатали. Я думаю, что вы имели undefinedв виду во второй части, а не'undefined'
страшно