Оператор ==
(нестрогое равенство) сравнивает значения с приведением типов, а оператор ===
(строгое равенство) сравнивает значения и типы без приведения:
5 == "5"; // true (приводит строку к числу)
5 === "5"; // false (разные типы)
null == undefined; // true (особый случай)
null === undefined; // false (разные типы)
0 == false; // true (приводит boolean к числу)
0 === false; // false (разные типы)
Рекомендация: Используйте ===
в 99% случаев для предсказуемого поведения.
// Сравнивает тип И значение
console.log(5 === 5); // true
console.log(5 === "5"); // false (разные типы)
console.log(true === true); // true
console.log(true === 1); // false (разные типы)
console.log(null === null); // true
console.log(undefined === undefined); // true
console.log(null === undefined); // false
// Сравнивает значения с приведением типов
console.log(5 == "5"); // true ("5" → 5)
console.log(true == 1); // true (true → 1)
console.log(false == 0); // true (false → 0)
console.log(null == undefined); // true (особый случай)
console.log("" == 0); // true ("" → 0)
Сравнение | == (нестрогое) | === (строгое) | Объяснение |
---|---|---|---|
5 == "5" | true | false | Строка приводится к числу |
true == 1 | true | false | Boolean приводится к числу |
false == 0 | true | false | Boolean приводится к числу |
null == undefined | true | false | Особое правило для == |
"" == 0 | true | false | Пустая строка приводится к 0 |
" " == 0 | true | false | Пробел приводится к 0 |
"0" == 0 | true | false | Строка приводится к числу |
[] == 0 | true | false | Массив приводится к строке, затем к числу |
[] == "" | true | false | Массив приводится к пустой строке |
[1] == 1 | true | false | Массив приводится к строке “1”, затем к числу |
===
// Примеры приведения типов
console.log("5" == 5); // "5" → 5, затем 5 == 5 → true
console.log(true == "1"); // true → 1, "1" → 1, затем 1 == 1 → true
console.log([1,2] == "1,2"); // [1,2] → "1,2", затем "1,2" == "1,2" → true
⚠️ Внимание! Эти сравнения могут привести к неожиданным результатам:
// Парадоксы с ==
console.log("" == 0); // true
console.log(" " == 0); // true
console.log("\n" == 0); // true
console.log("\t" == 0); // true
// Транзитивность нарушена!
console.log("" == 0); // true
console.log(0 == false); // true
console.log("" == false); // true, но логически странно
// Массивы
console.log([] == 0); // true
console.log([] == false); // true
console.log([] == ""); // true
// ❌ Плохо: неожиданное поведение
function checkValue(value) {
if (value == null) {
return "Пустое значение"; // Сработает для null И undefined
}
return "Есть значение";
}
console.log(checkValue(null)); // "Пустое значение"
console.log(checkValue(undefined)); // "Пустое значение"
console.log(checkValue(0)); // "Есть значение"
console.log(checkValue("")); // "Есть значение"
// ✅ Хорошо: явная проверка
function checkValueStrict(value) {
if (value === null || value === undefined) {
return "Пустое значение";
}
return "Есть значение";
}
// ✅ Или используйте nullish coalescing
const result = value ?? "значение по умолчанию";
// ❌ Плохо: неожиданные результаты
function validateAge(input) {
if (input == 18) {
return "Совершеннолетний";
}
return "Несовершеннолетний";
}
console.log(validateAge("18")); // "Совершеннолетний" (работает случайно)
console.log(validateAge(" 18 ")); // "Несовершеннолетний" (пробелы!)
console.log(validateAge(true)); // "Несовершеннолетний" (true != 18)
// ✅ Хорошо: явное приведение типов
function validateAgeStrict(input) {
const age = Number(input);
if (age === 18) {
return "Совершеннолетний";
}
return "Несовершеннолетний";
}
// ❌ Плохо: checkbox может быть строкой
function handleCheckbox(isChecked) {
if (isChecked == true) {
console.log("Отмечено");
}
}
handleCheckbox("true"); // Не сработает!
handleCheckbox(1); // Сработает (1 == true)
// ✅ Хорошо: явная проверка
function handleCheckboxStrict(isChecked) {
if (isChecked === true || isChecked === "true") {
console.log("Отмечено");
}
}
// ✅ Или приведение к boolean
function handleCheckboxBoolean(isChecked) {
if (Boolean(isChecked)) {
console.log("Отмечено");
}
}
В 99% случаев — это безопасный выбор:
// ✅ Безопасные сравнения
if (user.age === 18) { /* ... */ }
if (status === "active") { /* ... */ }
if (count === 0) { /* ... */ }
if (value === null) { /* ... */ }
if (typeof data === "string") { /* ... */ }
// ✅ В массивах и объектах
const users = ["admin", "user", "guest"];
if (users.includes("admin")) { /* ... */ } // includes использует ===
// ✅ В switch (использует ===)
switch (userRole) {
case "admin":
// ...
break;
}
Только когда точно знаете, что делаете:
// ✅ Проверка на null/undefined одновременно
if (value == null) {
// Сработает для null И undefined
console.log("Значение пустое");
}
// Эквивалентно:
if (value === null || value === undefined) {
console.log("Значение пустое");
}
// ✅ Но лучше использовать nullish coalescing
const result = value ?? "по умолчанию";
console.log(0 == false);
console.log(0 === false);
console.log("" == false);
console.log("" === false);
true
, false
, true
, false
0 == false
→ false
приводится к 0
, затем 0 == 0
→ true
0 === false
→ разные типы → false
"" == false
→ false
приводится к 0
, ""
приводится к 0
, затем 0 == 0
→ true
"" === false
→ разные типы → false
function isAdult(age) {
return age == 18;
}
console.log(isAdult("18")); // ?
console.log(isAdult(" 18")); // ?
console.log(isAdult(18.0)); // ?
true
, false
, true
Проблема: " 18"
(с пробелом) не равно 18
даже с приведением типов.
Решение:
function isAdult(age) {
return Number(age) === 18;
}
const arr = [];
console.log(arr == 0);
console.log(arr == "");
console.log(arr == false);
console.log(arr === false);
true
, true
, true
, false
[]
приводится к пустой строке ""
0
false
приводится к 0
==
дают true
===
сравнивает типы, поэтому false
// ❌ Проблемная функция
function findUser(users, id) {
return users.find(user => user.id == id);
}
const users = [
{ id: 1, name: "Анна" },
{ id: "2", name: "Борис" },
{ id: 3, name: "Вера" }
];
console.log(findUser(users, "1")); // Найдет Анну (1 == "1")
console.log(findUser(users, 2)); // Найдет Бориса ("2" == 2)
// ✅ Исправленная функция
function findUser(users, id) {
// Приводим id к строке для единообразия
const searchId = String(id);
return users.find(user => String(user.id) === searchId);
}
// Или приводим к числу
function findUserById(users, id) {
const searchId = Number(id);
return users.find(user => Number(user.id) === searchId);
}
// Или используем строгое сравнение с проверкой типа
function findUserStrict(users, id) {
return users.find(user => user.id === id);
}
function validateForm(data) {
// Найдите проблемы в этой валидации
if (data.name == "") {
return "Имя обязательно";
}
if (data.age == 0) {
return "Возраст обязателен";
}
if (data.isActive == false) {
return "Аккаунт должен быть активен";
}
return "Валидация пройдена";
}
// Тестовые данные
console.log(validateForm({ name: 0, age: false, isActive: "" }));
Проблемы:
name: 0
пройдет проверку name == ""
(0 != "")age: false
не пройдет проверку age == 0
(false == 0 → true)isActive: ""
не пройдет проверку isActive == false
("" != false)Исправленная версия:
function validateForm(data) {
if (typeof data.name !== "string" || data.name.trim() === "") {
return "Имя обязательно";
}
if (typeof data.age !== "number" || data.age <= 0) {
return "Возраст должен быть положительным числом";
}
if (data.isActive !== true) {
return "Аккаунт должен быть активен";
}
return "Валидация пройдена";
}
// ✅ Хорошо
if (status === "active") { /* ... */ }
if (count === 0) { /* ... */ }
if (user === null) { /* ... */ }
// ❌ Плохо
if (status == "active") { /* ... */ }
if (count == 0) { /* ... */ }
if (user == null) { /* ... */ }
// ✅ Хорошо: явное приведение
const userInput = "25";
const age = Number(userInput);
if (age === 25) { /* ... */ }
// ❌ Плохо: неявное приведение
if (userInput == 25) { /* ... */ }
// ✅ Хорошо: проверяем тип
function processValue(value) {
if (typeof value === "string" && value.length > 0) {
return value.toUpperCase();
}
if (typeof value === "number" && value > 0) {
return value * 2;
}
return null;
}
// ❌ Плохо: полагаемся на приведение типов
function processValueBad(value) {
if (value == true) {
return value.toUpperCase(); // Может упасть!
}
return value * 2;
}
// .eslintrc.json
{
"rules": {
"eqeqeq": ["error", "always"], // Запрещает ==
"no-implicit-coercion": "error" // Запрещает неявное приведение
}
}
// Object.is() еще строже чем ===
console.log(Object.is(NaN, NaN)); // true (=== дает false)
console.log(Object.is(-0, +0)); // false (=== дает true)
console.log(Object.is(5, 5)); // true
console.log(Object.is(5, "5")); // false
// Полезно для особых случаев
function isActuallyNaN(value) {
return Object.is(value, NaN);
}
// Вместо проверки на null/undefined
const result = value ?? "по умолчанию";
// Вместо
const result2 = (value === null || value === undefined)
? "по умолчанию"
: value;
// Безопасное обращение к свойствам
const city = user?.address?.city;
// Вместо
const city2 = user && user.address && user.address.city;
// ❌ Плохо
if (isActive == true) { /* ... */ }
if (hasPermission == false) { /* ... */ }
// ✅ Хорошо
if (isActive === true) { /* ... */ }
if (isActive) { /* ... */ } // Еще лучше
if (!hasPermission) { /* ... */ } // Для false
// ❌ Всегда false (сравниваются ссылки)
console.log([] == []); // false
console.log({} == {}); // false
// ✅ Правильное сравнение массивов
function arraysEqual(a, b) {
return a.length === b.length &&
a.every((val, i) => val === b[i]);
}
// ✅ Правильное сравнение объектов
function objectsEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b); // Простой способ
// Или используйте библиотеку lodash.isEqual
}
const obj = { count: 0, name: "" };
// ❌ Плохо: 0 и "" считаются false
if (obj.count) { /* ... */ } // Не сработает для 0
if (obj.name) { /* ... */ } // Не сработает для ""
// ✅ Хорошо: проверяем существование
if ("count" in obj) { /* ... */ }
if (obj.hasOwnProperty("count")) { /* ... */ }
if (obj.count !== undefined) { /* ... */ }
Золотые правила:
===
в 99% случаев — это безопасно и предсказуемо==
— он может привести к неожиданным результатамNumber()
, String()
, Boolean()
typeof
, Array.isArray()
, instanceof
??
, ?.
, Object.is()
Помните: Код должен быть понятным не только компьютеру, но и другим разработчикам. Строгое равенство ===
делает ваши намерения ясными и предотвращает ошибки.
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice(@AleksandrEmolov_EasyAdvice), добавляйте сайт в закладки и прокачивайтесь каждый день 💪