Даже маленькая ошибка в JavaScript часто выглядит как “все сломалось”. Ниже - самые типовые ситуации, которые встречаются в первых проектах. У каждой ошибки есть короткий пример и понятное исправление.
1) Переменная не объявлена: ReferenceError
Проблема: обращение к имени, которого нет.
console.log(total);
Исправление: объявить переменную или проверить имя.
let total = 0;
console.log(total);
2) Путаница == и ===
Проблема: == приводит типы и может дать неожиданные совпадения.
console.log(0 == false);
console.log("" == 0);
Исправление: для предсказуемости использовать ===.
console.log(0 === false);
console.log("" === 0);
3) Строка вместо числа (особенно из input)
Проблема: значения из формы часто строки.
const a = "2";
const b = "3";
console.log(a + b);
Исправление: приводить к числу.
const a = Number("2");
const b = Number("3");
console.log(a + b);
4) NaN: число “не получилось”
Проблема: Number("abc") дает NaN, и дальше вычисления тоже станут NaN.
const value = Number("abc");
console.log(value);
console.log(value + 10);
Исправление: проверять Number.isNaN.
const value = Number("abc");
if (Number.isNaN(value)) {
console.log("Bad number");
} else {
console.log(value + 10);
}
5) Неверное условие: = вместо ===
Проблема: присваивание внутри условия.
let status = "ok";
if (status = "fail") {
console.log("error");
}
Этот код присваивает "fail" и условие становится истинным (строка truthy).
Исправление: сравнение.
let status = "ok";
if (status === "fail") {
console.log("error");
}
6) Off-by-one в циклах: границы
Проблема: неправильное условие, выход за пределы.
const items = ["a", "b", "c"];
for (let i = 0; i <= items.length; i++) {
console.log(items[i]);
}
На последнем шаге items[i] будет undefined.
Исправление: i < items.length.
const items = ["a", "b", "c"];
for (let i = 0; i < items.length; i++) {
console.log(items[i]);
}
7) Бесконечный цикл: забыли шаг
Проблема: счетчик не меняется.
let i = 0;
while (i < 3) {
console.log(i);
}
Исправление: менять i.
let i = 0;
while (i < 3) {
console.log(i);
i++;
}
8) Мутации массива: “исчезло само”
Проблема: методы вроде sort, reverse, splice меняют исходный массив.
const nums = [10, 2, 30];
const sorted = nums.sort((a, b) => a - b);
console.log(nums);
console.log(sorted);
И nums, и sorted будут отсортированы, потому что это один и тот же измененный массив.
Исправление: копировать перед изменением.
const nums = [10, 2, 30];
const sorted = [...nums].sort((a, b) => a - b);
console.log(nums);
console.log(sorted);
9) Ошибка с объектами: сравнение по ссылке
Проблема: объекты сравниваются по ссылке, не по содержимому.
console.log({ a: 1 } === { a: 1 });
Исправление: сравнивать конкретные поля или писать свою проверку.
const x = { a: 1 };
const y = { a: 1 };
console.log(x.a === y.a);
10) undefined из объекта: несуществующее свойство
Проблема: опечатка или нет поля.
const user = { name: "Ann" };
console.log(user.age);
Исправление: проверка или безопасный доступ.
const user = { name: "Ann" };
if ("age" in user) {
console.log(user.age);
} else {
console.log("No age");
}
11) DOM: querySelector вернул null
Проблема: селектор не нашел элемент, а код сразу читает свойства.
const title = document.querySelector(".title");
console.log(title.textContent);
Исправление: проверять null и корректность селектора.
const title = document.querySelector(".title");
if (title) {
console.log(title.textContent);
}
12) innerHTML и пользовательские данные
Проблема: вставка строки, которая может содержать HTML.
const el = document.querySelector("#out");
const name = "<img src=x onerror=alert(1)>";
el.innerHTML = name;
Исправление: для текста использовать textContent.
const el = document.querySelector("#out");
const name = "<img src=x onerror=alert(1)>";
el.textContent = name;
13) addEventListener: вызвать функцию вместо передать
Проблема: обработчик вызывается сразу.
function onClick() {
console.log("click");
}
const btn = document.querySelector("#btn");
btn.addEventListener("click", onClick());
Исправление: передать функцию без скобок.
function onClick() {
console.log("click");
}
const btn = document.querySelector("#btn");
btn.addEventListener("click", onClick);
14) Асинхронность: забыли await
Проблема: вместо данных получается Promise.
async function load() {
const data = fetch("/api/data").then((r) => r.json());
console.log(data);
}
load();
Исправление: поставить await.
async function load() {
const data = await fetch("/api/data").then((r) => r.json());
console.log(data);
}
load();
15) fetch: считать, что 404 - это ошибка промиса
Проблема: fetch отклоняется (reject) в основном на сетевых ошибках. HTTP 404 - это обычный ответ, но response.ok будет false.
async function loadJSON(url) {
const response = await fetch(url);
return response.json();
}
Исправление: проверять ok.
async function loadJSON(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
return response.json();
}
16) try/catch не ловит ошибку без await
Проблема: ошибка в промисе не попадет в catch, если промис не awaited внутри try.
async function run() {
try {
fetch("/api/data").then(() => {
throw new Error("fail");
});
} catch (e) {
console.log("caught");
}
}
run();
Исправление: использовать await или ловить catch у промиса.
async function run() {
try {
await fetch("/api/data").then(() => {
throw new Error("fail");
});
} catch (e) {
console.log("caught");
}
}
run();
17) import/export: перепутать default и named
Проблема: default экспорт импортируется без фигурных скобок.
sum.js
export default function sum(a, b) {
return a + b;
}
Неправильно:
import { sum } from "./sum.js";
Правильно:
import sum from "./sum.js";
18) “Склейка” логики и UI в одном месте
Проблема: обработчик делает все подряд: запрос, преобразование, отрисовку, сохранение, валидацию.
btn.addEventListener("click", async () => {
const response = await fetch("/api/items");
const items = await response.json();
const filtered = items.filter((x) => x.active);
document.querySelector("#out").textContent = String(filtered.length);
localStorage.setItem("lastCount", String(filtered.length));
});
Исправление: выносить по смыслу в функции и файлы (data/logic/ui), а в обработчике оставлять склейку.
async function loadItems() {
const response = await fetch("/api/items");
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
return response.json();
}
function countActive(items) {
return items.filter((x) => x.active).length;
}
function renderCount(root, count) {
root.textContent = String(count);
}
btn.addEventListener("click", async () => {
try {
const items = await loadItems();
const count = countActive(items);
renderCount(document.querySelector("#out"), count);
localStorage.setItem("lastCount", String(count));
} catch (e) {
console.log(e.message);
}
});
Что стоит запомнить
- Большая часть ошибок новичков - это типы (строка вместо числа), границы циклов и
nullиз DOM. ===дает предсказуемые сравнения.- Методы
sort/splice/reverseмутируют массив. - В fetch важно проверять
response.ok. - Чтение сообщения ошибки и stack trace обычно дает ответ быстрее, чем догадки.