Проблема почти всех вводных статей про JavaScript — они одновременно про язык 1995 года и про язык, на котором сегодня пишут в продовой кодовой базе. Прототипное наследование, var, IIFE-паттерны, callback-hell, then-цепочки, jQuery-стиль — всё это лежит рядом с современным async/await, модулями и стрелочными функциями. Джун, который прочитал такой обзор, знает много, но не знает главного: что из этого реально писать.
Этот обзор устроен наоборот. Здесь — только то, что видно в код-ревью на реальном проекте: modern-only синтаксис, современные подходы, никакой археологии. Всё, что помечено «устаревшее, но встречается в legacy», вынесено в один короткий раздел и упомянуто одной строчкой. Задача — за 15 минут дать понимание, каким JavaScript пишут сейчас, а не каким его можно писать теоретически.
Как выглядит современный JS-код
Прежде чем разбирать «по кирпичикам», посмотрим, как выглядит типичный кусок современного JavaScript. Функция получает URL, тянет данные через fetch, обрабатывает результат:
import { formatDate } from './utils.js';
const fetchPosts = async (userId) => {
const url = `/api/users/${userId}/posts`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const posts = await response.json();
return posts.map(({ id, title, createdAt }) => ({
id,
title,
date: formatDate(createdAt),
}));
};
export { fetchPosts };
Здесь укладываются восемь ключевых идей современного JavaScript: import/export (ES-модули), const вместо var, стрелочная функция, async/await вместо then-цепочек, template literals с ${}-интерполяцией, деструктуризация в аргументах map, shorthand-свойства объекта, fetch API вместо XMLHttpRequest. Всё это — ES6+ (стандарт 2015 года и его расширения), выучив это, ты уже пишешь на 90% современный JS.
Основы: переменные, типы, функции
Переменные — только const и let. var в новом коде не используется никогда. Дефолт — const: значение не переприсваивается. Если переприсваивается — let. Простое правило.
const name = 'Аня'; // не будет переприсваиваться
let counter = 0; // будет меняться в цикле
counter += 1; // OK
name = 'Борис'; // TypeError: Assignment to constant
Типы данных. В JavaScript 8 типов: 7 примитивов и 1 объектный. Примитивы: string, number, boolean, null, undefined, bigint (для очень больших целых), symbol (для уникальных идентификаторов — в рeальном коде встретишь редко). Всё остальное (массивы, объекты, функции, даты) — это object. Проверить тип — через typeof x, но не для null (там историческая ошибка — typeof null === 'object').
Функции — в основном стрелочные. Классические function-объявления остались в двух местах: для методов класса и когда нужно своё this (в обработчиках событий, например). В остальных случаях — стрелочные:
// classic — только для методов класса
class User {
constructor(name) { this.name = name; }
greet() { return `Hi, ${this.name}`; }
}
// стрелочная — для всего остального
const double = (n) => n * 2;
const sum = (a, b) => a + b;
// с многострочным телом — фигурные скобки и явный return
const process = (items) => {
const filtered = items.filter(x => x > 0);
return filtered.length;
};
Работа с данными: массивы, объекты, деструктуризация
Массивы обрабатываются через методы, не циклы. Классический for остаётся редко — для сложных случаев или performance-критичного кода. В остальных случаях используются map (преобразование), filter (фильтрация), reduce (агрегация), find, some, every, forEach (для side effects):
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map(n => n * 2); // [2, 4, 6, 8, 10]
const evens = nums.filter(n => n % 2 === 0); // [2, 4]
const sum = nums.reduce((acc, n) => acc + n, 0); // 15
const first = nums.find(n => n > 3); // 4
const hasEven = nums.some(n => n % 2 === 0); // true
Деструктуризация — вытаскиваем поля из объекта одной строкой. Заменяет громоздкие const name = user.name; const email = user.email;:
const user = { name: 'Аня', email: 'a@example.com', age: 28 };
// деструктуризация объекта
const { name, email } = user;
// с переименованием и дефолтами
const { name: userName, city = 'unknown' } = user;
// деструктуризация в аргументах функции
const greet = ({ name, age }) => `${name}, ${age} лет`;
greet(user); // "Аня, 28 лет"
// деструктуризация массива
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3, 4, 5]
Spread-оператор — копирование и слияние. Три точки перед именем массива/объекта распаковывают его содержимое:
const a = [1, 2, 3];
const b = [...a, 4, 5]; // [1, 2, 3, 4, 5]
const user = { name: 'Аня' };
const updated = { ...user, age: 28 }; // { name: 'Аня', age: 28 }
// иммутабельный update — не мутируем оригинал
const users = [{ id: 1, name: 'Аня' }, { id: 2, name: 'Борис' }];
const withNew = [...users, { id: 3, name: 'Вика' }];
Асинхронный код: async/await и fetch
Асинхронность в современном JS — это async/await. Классические .then()/.catch()-цепочки остались в старом коде, но новые запросы почти всегда пишут через async-функции.
// современно
const loadUser = async (id) => {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (err) {
console.error('Failed to load user', err);
return null;
}
};
// legacy-версия — так уже не пишут в новом коде
function loadUserOld(id) {
return fetch(`/api/users/${id}`)
.then(r => {
if (!r.ok) throw new Error('HTTP');
return r.json();
})
.catch(err => {
console.error(err);
return null;
});
}
Ключевое правило: await работает только внутри async-функций. Если пишешь const x = await something() в теле не-async-функции — синтаксическая ошибка. В верхнем уровне модуля (top-level await) он разрешён с 2022 года — удобно для инициализации.
Модули и код-организация
Современный JavaScript пишется в ES-модулях: import и export. Формат CommonJS (require(), module.exports) остаётся в legacy Node-коде, но новые проекты — ESM.
// utils.js
export const formatDate = (isoString) => {
return new Date(isoString).toLocaleDateString('ru-RU');
};
export const capitalize = (str) => str[0].toUpperCase() + str.slice(1);
// или default export для главной сущности модуля
export default class User { /* ... */ }
// app.js
import { formatDate, capitalize } from './utils.js';
import User from './User.js';
import * as helpers from './helpers.js'; // всё содержимое как объект
В браузере ES-модули работают через <script type="module" src="app.js"></script>. В Node.js — либо через файлы с расширением .mjs, либо через "type": "module" в package.json.
TypeScript как естественный next-step
В индустрии новые проекты почти никогда не пишут в чистом JavaScript — сразу TypeScript. По State of JS 2023 более 84% новых проектов запускаются в TS. Разница минимальная в синтаксисе:
// JavaScript
const fetchUser = async (id) => {
const r = await fetch(`/api/users/${id}`);
return r.json();
};
// TypeScript — то же, но с явными типами
interface User {
id: number;
name: string;
email: string;
}
const fetchUser = async (id: number): Promise<User> => {
const r = await fetch(`/api/users/${id}`);
return r.json();
};
Node.js с 22-й версии запускает .ts-файлы нативно (через --experimental-strip-types); Deno и Bun — и вовсе TypeScript-first, никаких флагов не нужно. Так что мостик от «выучил JS» к «пишу на TS» сейчас короче, чем когда-либо. Подробнее про переход — в отдельной статье про TypeScript.
Инструменты для стартера
Не все инструменты equal — за годы накопилось много альтернатив, но актуальный порядок такой:
- Редактор: VS Code — дефолт индустрии, бесплатный, TypeScript-plugin из коробки. Cursor — форк VS Code с AI-агентом в комплекте, набирает популярность.
- Рантайм для запуска: Node.js — дефолт для legacy, актуален для всего LTS. Bun — в 3-4 раза быстрее Node, всё встроено (bundler, test-runner, TypeScript). Deno — TypeScript-first, лучший DX.
- Package manager: npm идёт с Node из коробки. pnpm экономит место на диске и быстрее. bun install — ещё быстрее.
- Bundler для веба: Vite — дефолт для новых проектов (React, Vue, Svelte). Быстрый dev-сервер, лёгкая настройка.
- Первый фреймворк: React остаётся самым востребованным на рынке. Vue — более linear learning curve. Svelte 5 — наименьший boilerplate. Astro — для content-сайтов.
Что дальше изучать
После этого обзора логичный маршрут — углубляться, а не расширяться. Порядок такой:
- Работа с DOM и события: querySelector, addEventListener, event bubbling — на реальных примерах обычно осваивается за неделю практики
- Promises и Promise.all / Promise.race: хотя async/await покрывает 90% случаев, эти два метода нужны для параллельных запросов
- TypeScript-типы: базовые (string, number, boolean), интерфейсы, union-типы ('a' | 'b'), дженерики
- Первый фреймворк (React или Vue): компоненты, состояние, эффекты — на JS-практике осваиваются за месяц
- Тестирование: Vitest (для новых проектов) или Jest — unit-тесты входят в стандарт индустрии
Что осознанно не стоит изучать сразу: прототипное наследование в глубину, this-биндинг в стрелочных vs обычных функциях (в реальном коде почти не встречается благодаря arrow-функциям), IIFE, callback-паттерны — в современной практике всё это либо ушло, либо покрыто более удобными абстракциями. Если что-то из этого встретишь в legacy — посмотришь, поймёшь, продолжишь. Учить с нуля «чтобы знать» — трата времени.
Комментарии (0)