Отличия == и === в JavaScript

Сразу предупрежу, да, статья немного некорректная, добро пожаловать в комментарии, там неплохие уточнения ).

Доброго времени суток.


В JavaScript есть два похожих оператора: == и ===. Если не знать их отличия, это может обернуться кучей ошибок. Так что решил раскрыть эту тему. Чем именно отличаются == и ===, как они работают, почему так происходит, и как избежать ошибок.

Оператор == сравнивает на равенство, а вот === — на идентичность. Плюс оператора === состоит в том, что он не приводит два значения к одному типу. Именно из-за этого он обычно и используется.

abc  == undefined;	// true, если abc = undefined | null
abc === undefined;	// true - только если abc = undefined!

abc  == false;	// true, если abc = false | 0 | '' | []
abc === false; // true, только если abc = false!

Ведь путать false и 0 (или '', или []) — вряд ли очень хорошо.

Разумеется:
5 === 5;	// true
true === true;	// true
'abc' === 'abc';	// true


А теперь интересный пример.
5  == 5;					// true
5 === 5;					// true
new Number(5)  == 5;				// true
new Number(5) === 5;				// false!


Почему так происходит? Да, любое число — это объект класса Number. Но можно представить число как цифру — некоторой константой. Она единожды объявлена, и всегда идентична сама себе. Но в то же время объявляя новый объект класса Number — он равен ей по значению, но не идентичен (так как это два совершенно разных объекта класса Number).

Arrays / Objects


А вот для массивов и объектов оба оператора работают одинаково, сравнивая на идентичность:
var a = {};
a  == {};	// false
a === {};	// false
a  == a;	// true
a === a;	// true


Для сравнения массивов и объектов можно написать специальную функцию:
function isEq(a, b){
	if(a == b) return true;
	for(var i in a){
		if(!isEq(a[i], b[i])) return false;
	}
	for(var i in b){
		if(!isEq(a[i], b[i])) return false;
	}
	return true;
}

Немножко неаккуратно, два цикла, да и про hasOwnProperty забыли; ну да сойдёт.

This <-


Есть ещё один подводный камень. Это передача в this.
(function(){
	this  == 5;	// true
	this === 5;	// false
}).call(5);


Вот такой вот момент. Стоит о нём не забывать.

Итого...


Ну а теперь представим, что мы пишем свой суперфреймворк, активно юзаем там оператор === вместо == просто потому что он красивее, и некто находит у нас несколько багов.
func(new Number(5));
(function(){
	func(this);
}).call(5);


Кажется, что такие примеры нежизнеспособны? Пожалуйста!

jQuery:
$.each([1, 2, 3, 4, 5], function(){
	func(this);
});


Ну или захотелось расширить цифру.
var Five = new Number(5);
Five.a = 2; // захотелось расширить, а просто 5 не расширяется
// здесь как-то используем...
func(Five);


На этом всё, надеюсь кому-то будет полезно. Спасибо за внимание.

UPD. Спасибо за ссылку vermilion1, JS Гарден.
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 68
  • +2
    На мой взгляд, проще прочитать раздел приведения типов (вроде так) в книге JavaScript The Definitive Guide, даже на русском, там подробно расписано про этот момент, а так же про .toString() и другие вещи.
    • +5
      Получилась очередная статья по основам js (причем короткая и не полная), коих предостаточно в сети.
      • +5
        Статья получилась не только неполная, но местами и некорректная. Кроме этого, не сказано главное и основное: оператор строгого равенства "===" не проверяет на «идентичность», такого понятие в языке нет. В действительности этот оператор на самом деле сначала проверяет тип (typeof), а потом проверяет значение. Одного этого достаточно чтобы в любом случае точно предсказать результат.
        • +4
          > Одного этого достаточно чтобы в любом случае точно предсказать результат.

          Нет, этого недостаточно:

          var a = {}, b = {};
          console.log(a === b);
          • 0
            Тут уместна другая аксиома ООП: два объекта никогда не будут равны, даже если равны значения всех свойств объектов.
            • 0
              В ООП нет таких аксиом, поправьте?

              В множестве современных языков оператор сравнения для объектов вполне себе перегружается.
              • 0
                метод .equals() или оператор == может и перегружается, а где вы видели перегружающийся оператор ===?
                • 0
                  А в каких языках, где есть перегрузка методов, вы видели ===?

                  Плюс вопрос об аксиомах до сих пор в силе
                  • 0
                    err, перегрузка операторов
      • +2
        У меня тоже возникло ощущение, что либо после этого:
        «Оператор == сравнивает на равенство, а вот === — на идентичность. Плюс оператора === состоит в том, что он не приводит два значения к одному типу. Именно из-за этого он обычно и используется.»
        нужно было остановиться, либо же расписывать очень подробно
        • 0
          Полностью с Вами согласен. Вот думаю, после прочтения таких статей, давно уже изъезженных, а самое главное хорошо описанных в спецификации, думаешь, ну неужели в javascript нету куда более тонких мест чем, точно хорошо описано его создателями. Мне куда интереснее было бы почитать, про разное поведение в разных движках языка(не только браузерных). Уж лучше написать статью про определение типов написать, хотя это тоже тема разобрана по кусочкам.
          • 0
            Я в скором времени буду писать про knockout.js и некоторые недокументированные особенности.
        • +11
          > Так что решил полностью раскрыть эту тему
          полностью? да ладно.
          Например, что вернёт «NaN === NaN», или как кастятся объекты, +0 тоже самое что и -0? и т.д.
          Там в принципе тонкости есть свои.
        • –4
          Когда увидел название статьи, хотел было сказать, что такой узкой темы маловато для статьи, но раскрыто шикарно, спасибо!
          • +2
            abc === undefined;	// true - только если abc = undefined!

            Вот так точно делать не стоит. undefined не зарезервированное слово, поэтому нужно проверять как typeof === 'undefined'
            • +2
              (function(undefined) {
                // тут правильный undefined
              })();
              
              • +1
                бесспорно, но об этом нигде не упомянуто
                • 0
                  Об этом знают, например, все, кто когда-либо читал исходный код jQuery.
                  • 0
                    Не только, это очень много где используется.
                • –5
                  Зачем, если итак работает?
                  • +2
                    Дело в том что undefined это не совсем ключевое слово и может быть переопределено. Ничто не мешает одному из скриптом на странице выполнить такой код:
                    undefined = true
                    • –3
                      Это да, но это актуально только если пишешь библиотеку/фреймворк или не доверяешь собственному коду.
                      Кстати есть более краткая форма, которую я, правда, нигде больше не видел:
                      var undef;
                      // undef — правильный undefined
                      • 0
                        Да назовите как угодно, это будет просто имя нигде не определенной переменной.
                        • +1
                          А вы доверяете своему коду? Зря.
                          • –3
                            * если не доверяешь собственной голове
                            • +2
                              А ежели над проектом работает не только ваша голова?
                        • +1
                          undefined — это свойство global (он же window), у которого Writable == false (менять не можем значение) и Configurable == false (не можем его удалить).
                  • +1
                    А можно еще так:
                    abs === void 0
                    • 0
                      abs === eval()
                      abs === [].pop()
                      abs === {}.a

                      Последние 2 конечно можно подпортить модификацией прототипа Object и Array
                      • 0
                        Object.prototype.toString.call( var ) === '[object Undefined]'
                        • +1
                          void 0 используется кстати в CoffeeScript, он подменяет все вхождения undefined этим значением.
                      • –1
                        Не поверите, но такая конструкция используется даже в jQuery.
                      • 0
                        Мне просто интересно. А вот так стоит делать?:

                        abc === void 0;

                        void зарезервированное слово
                      • 0
                        У вас один знак равенства лишний — typeof всегда возвращает строку.
                      • +5
                        a == a; // true
                        a === a; // true
                        ну это уже совсем.
                        В целом — статья достаточно пустословная. Просто раскрытое понятие, что делает абсолютное сравнение (а именно — проверка без приведения). Любой, кто изучал js чуть глубже, чем $('.hide').click(function(){$(this).hide())}) (да и не только js, достаточно много языков, где есть такая же конструкция), знает в чем разница. А действительно сложные и спорные моменты не раскрыты
                          • 0
                            Просто для меня это как-то элементарно было… Но ссылку в статье привёл. И кстати я объяснил, почему new Number(10) === 10 возвращает false. В JS garden этого нету
                            • +1
                              Не спорю. Ссылку оставил потому, что она полезная, притензий к вам никаких нет :)
                          • +1
                            Вы еще на главной про объявления переменных расскажите.
                            • +3
                              abc  == undefined;	// true, если abc = undefined | null | 0 | false | '' | а также [] в IE6
                              

                              Заблуждение из PHP. Смотрим:
                              console.log( 0     == undefined ); // false
                              console.log( false == undefined ); // false
                              console.log( ''    == undefined ); // false
                              


                              undefined нестрого равен только null и наоборот.
                              • 0
                                Да я полностью с Вами согласен, и об этом нам говорит спецификация es5.github.com/#x11.9.3, в которой не так много уж много букв, чтобы не осилить ее( в частности по поводу операторов).
                                • +1
                                  Почитайте, там много интересного. Вот, например, одна из прекрасных пудр для мозгов:

                                  Number.prototype.a = function(){}();
                                  Number.b = 7;
                                  
                                  var a = 123,
                                      b = Infinity,
                                      c = new Number(b);
                                  
                                  c.b = b.b = a.a = true;
                                  
                                  console.log(typeof a.a); // ?
                                  console.log(typeof b.b); // ?
                                  console.log(typeof c.a); // ?
                                  console.log(typeof c.b); // ?
                                  • 0
                                    Ай-ай-ай, подловили на вызове функции. Неплохой quiz, спасибо.
                                    Если кому-то сложно понять почему получаются именно такие результаты, советую ознакомится с серией статей Тонкости ECMA-262-3 на сайте dmitrysoshnikov.com/. Первая часть тут.
                                    • 0
                                      Спойлерну пожалуй.
                                      Собственно 'X.Y' резолвится как Reference(base: 'X', refname: 'Y').GetValue().
                                      «Конструктор» тупо заполняет внутренние члены класса Reference, основная логика ложится на GetValue().
                                      Примитивы (Type(123) и Type(Infinity) == Number (не, не класс)) конвертятся в объекты Number.
                                      Но фишка в том, что конвертятся только во время выполнения данного метода, то есть a.a = true проходит вполне нормально. Но после выполнения a становится снова примитивом.
                                      На время второго обращения он снова превращается в объект, но это новый объект, без заполненного свойства. Если бы нас не пытались дурить (Number.b = 7, хаха), а написали правильно через прототип, то вторая строчка была бы number.
                                      Это что касается первых двух выводов в лог.
                                      Для резолва свойства объекта управление переходит на внутренний метод типа Object — Get().
                                      Который зовёт GetPropery() и в нашем случае возвращает два дескриптора — для ".a" и ".b", для второго случая всё понятно — вернётся четко «true», подводных камней нет.
                                      А на первом опять ловушка на невнимательность. Это FE с одновременным вызовом функции, замыкание, да. Ну и всем, надеюсь, известно, что по-дефолту без return'a функция возвращает undefined.
                                      А typeof undefiend == 'undefined'.
                                      PS: как-то так, возможно наврал в именах функций.
                                      • +1
                                        Как-то много букв, но на самом деле используется только одна фишка — «забывчивость» on-the-fly объекта, всё остальное — тупо на внимательность.
                                  • 0
                                    Спасибо, поправил. Это верно не для undefined, а для false. Заодно немножко дополнил статью.
                                    • 0
                                      И опять неправильно написали!
                                      abc  == false;	// true, если abc = undefined | null | false | 0 | '' | а также [] в IE6

                                      var abc = undefined;
                                      console.log(abc == false);// false
                                      • 0
                                        Прошу прощения, действительно. С null и массивами тоже поправил
                                  • –4
                                    Напишите про конкатенацию, пожалуйста. Не разберу в чём различие += и -=.
                                    • 0
                                      var a = 5;
                                      a += 2; // теперь a = 7
                                      a -= 5; // теперь a = 2
                                      
                                      a + 2; // возвращает 4 (2 + 2), при этом a = 2
                                      a - 1; // возвращает 1 (2 - 1), при этом a = 2 до сих пор

                                      • +1
                                        Здесь должна быть табличка с надписью «сарказм».
                                    • +10
                                      Вашу функцию:

                                      function isEq(a, b){
                                      if(a == b) return true;
                                      for(var i in a){
                                      if(!isEq(a[i], b[i])) return false;
                                      }
                                      for(var i in b){
                                      if(!isEq(a[i], b[i])) return false;
                                      }
                                      return true;
                                      }

                                      Можно переписать гораздо короче:

                                      function isEq(){return true}

                                      Результат будет тот же :)
                                    • 0
                                      захотелось расширить, а просто 5 не расширяется


                                      5..property = "value";
                                      • 0
                                        Другой разговор, что прочитать это потом нельзя! расширить можно.
                                        • 0
                                          var a = 5;
                                          a.abc = 10;
                                          a.abc; // undefined
                                          
                                          a = new Number(5);
                                          a.abc = 10;
                                          a.abc; // 10


                                          То же самое с String и Boolean.
                                        • 0
                                          Мои пять копеек
                                          new Number(5).valueOf() === 5; // true
                                          • 0
                                            Да, любое число — это объект класса Number.

                                            Нет, на самом деле в JS есть простые типы. Число 5 — это простой тип, а вот new Number(5) — это объект класса Number. Поэтому они и не равны при строгом сравнении.
                                            • 0
                                              Плюс:
                                              typeof 5; // number
                                              typeof new Number; // object
                                            • 0
                                              Что касается «передачи в this» — там происходит автоматический боксинг примитивных типов, соответственно this будет содержать соответствующий объект (аля new Number(5)).

                                              Но тут тоже есть ньюанс — в strict mode this может быть примитивным типом (автоматического боксинга там не будет):

                                              (function() { console.log(this === 5); }).call(5) // false
                                              (function() { "use strict"; console.log(this === 5); }).call(5) // true

                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                              Самое читаемое