Веб-разработка

JavaScript: основы языка

1995 год. Netscape. Брендан Эйх пишет JavaScript за 10 дней - язык, который должен был быть «скриптованием для не-программистов». Тридцать лет спустя: 98% всех сайтов в мире, Node.js обрабатывает запросы Netflix со скоростью 70 тысяч в секунду. `typeof null === 'object'` - баг, пережившей браузерные войны. Понимание «почему» превращает ловушки JS в суперсилу.

  • **Netflix** перешёл на Node.js и сократил время запуска приложения на 70% - благодаря event loop и неблокирующему I/O
  • **React hooks** (`useState`, `useEffect`) работают на замыканиях - каждый рендер создаёт новое замыкание с текущим состоянием
  • **Cloudflare Workers** выполняют JavaScript на edge-серверах по всему миру с cold start меньше 5мс - быстрее Lambda

Netscape, 1995: язык написанный за 10 дней

В мае 1995 года Брендан Эйх получил задачу от Netscape: создать «язык для склейки» HTML-элементов в браузере. Дедлайн - 10 дней. Он создал прототип, взяв синтаксис из Java, функции первого класса из Scheme и прототипное наследование из Self. typeof null === "object" - артефакт той спешки. Язык назвали Mocha, потом LiveScript, потом - в маркетинговых целях, ничего общего с Java - JavaScript. В 1997 ECMAScript-стандарт. В 2009 Node.js. В 2015 ES6 с классами. В 2023 - 98% сайтов мира.

Предварительные знания

  • CSS: от каскада до Grid

Типы данных

**JavaScript - язык с динамической типизацией.** Переменная может сначала хранить число, потом строку, потом объект. Это даёт гибкость, но и порождает коварные баги. Понимание системы типов - первый шаг к предсказуемому коду.

В JavaScript **7 примитивных типов** и **1 ссылочный** (object). Примитивы - неизменяемые значения, которые сравниваются по значению. Объекты - изменяемые структуры, которые сравниваются по ссылке.

ТипПримерыtypeof
string"hello", 'world', `template`"string"
number42, 3.14, NaN, Infinity"number"
booleantrue, false"boolean"
nullnull"object" (баг с 1995 года!)
undefinedundefined"undefined"
symbolSymbol('id')"symbol"
bigint9007199254740991n"bigint"
object{}, [], function(){}, new Date()"object" или "function"

**`typeof null === "object"`** - это не фича, это баг из первой версии JavaScript (1995). Брендан Эйх написал язык за 10 дней, и этот баг сохранился ради обратной совместимости. Для проверки на null используется `value === null`.

**Правило на всю карьеру:** всегда использовать `===` вместо `==`. Оператор `==` выполняет неявное приведение типов по сложным правилам, которые не знает наизусть ни один опытный разработчик. `===` сравнивает без приведения - предсказуемо и безопасно.

Что выведет `console.log(typeof null)`?

Замыкания

**Замыкание - одна из самых важных концепций JavaScript.** Каждый раз, когда используется callback, обработчик события или паттерн модуля - это замыкание. Суть: **функция запоминает переменные из места, где была создана**, даже если выполняется в совершенно другом контексте.

**Lexical Environment** - объект, который движок JavaScript создаёт при каждом вызове функции. Хранит локальные переменные и ссылку на внешнее окружение. Замыкание - это функция плюс ссылка на лексическое окружение, в котором она была создана.

**Классическая ловушка с циклами:** `for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); }` выведет `3, 3, 3` - все функции замкнули одну переменную `i`. Решение: использовать `let` вместо `var` - `let` создаёт новое окружение на каждой итерации.

Замыкания - фундамент для React hooks (`useState`, `useEffect`), Express middleware, Redux и практически любого JavaScript-паттерна. Понимание замыканий = понимание того, как все эти инструменты работают изнутри.

Что выведет код: `for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }` ?

Прототипы

**В JavaScript нет классов в классическом смысле.** Ключевое слово `class` (ES6) - это синтаксический сахар над прототипным наследованием. У каждого объекта есть скрытая ссылка `[[Prototype]]` на другой объект. Когда обращаются к свойству, которого у объекта нет, JavaScript ищет его по **цепочке прототипов**.

**`__proto__`** - геттер/сеттер для доступа к `[[Prototype]]` объекта (нестандартный, но повсеместно поддерживаемый). **`.prototype`** - свойство функций-конструкторов, которое становится `[[Prototype]]` для объектов, созданных через `new`. Не путать: `__proto__` есть у каждого объекта, `.prototype` - только у функций.

**Частый вопрос на интервью:** «В чём разница между `class` и функцией-конструктором?» Ответ: `class` строже - нельзя вызвать без `new`, методы не перечисляемые, всегда в strict mode. Но механизм наследования идентичен - та же цепочка прототипов.

Что произойдёт при обращении к свойству, которого нет ни у объекта, ни у его прототипов?

Event Loop

**JavaScript выполняет код в одном потоке.** Но при этом Node.js обрабатывает тысячи параллельных HTTP-запросов и не замораживает UI в браузере. Как? Через **event loop** - механизм, координирующий выполнение синхронного кода, callbacks, promises и I/O-операций. Netflix перешёл на Node.js и сократил время запуска приложения на 70% - именно благодаря этой архитектуре.

**Ключевое правило:** микро-задачи (Promise, queueMicrotask, MutationObserver) имеют приоритет над макро-задачами (setTimeout, события). Все микро-задачи выполняются перед следующей макро-задачей. Значит `Promise.resolve().then(fn)` выполнится раньше `setTimeout(fn, 0)`.

**`setTimeout(fn, 0)` не означает «выполнить через 0мс».** Это означает «добавить в очередь задач после текущего стека и всех микро-задач». Реальная задержка - минимум 4мс (ограничение спецификации HTML5), плюс время ожидания в очереди.

В **Node.js** есть дополнительная фаза: `process.nextTick()` выполняется даже раньше Promises. Приоритет: Call Stack - process.nextTick - Microtasks (Promise) - Macrotasks (setTimeout). В браузере nextTick не существует.

JavaScript однопоточный, значит он медленный и не подходит для высоконагруженных серверов

Однопоточность JavaScript компенсируется event loop и неблокирующим I/O. Node.js обрабатывает тысячи параллельных соединений в одном потоке, потому что 90% времени сервер ожидает I/O (БД, сеть, файлы), а не вычисляет

Для I/O-bound задач (веб-серверы, API, real-time приложения) event loop эффективнее модели «один поток на запрос» (Java/PHP). Netflix, LinkedIn, PayPal перешли на Node.js именно ради производительности. Для CPU-bound задач (рендеринг, ML) JavaScript действительно уступает - но для этого есть Worker Threads

В каком порядке выполнятся: `console.log('A')`, `setTimeout(() => console.log('B'), 0)`, `Promise.resolve().then(() => console.log('C'))`?

Ключевые идеи

  • **7 примитивов + object.** Примитивы - по значению, объекты - по ссылке. `===` вместо `==` всегда. `typeof null === 'object'` - баг с 1995 года
  • **Замыкание = функция + лексическое окружение.** Счётчик, модульный паттерн, частичное применение. `let` вместо `var` в циклах. Замыкания - фундамент React hooks
  • **Цепочка прототипов - наследование в JavaScript.** `class` - сахар над прототипами. `Object.create()` для прямого создания цепочки. Свойство ищется по цепочке до null
  • **Event loop: microtask > macrotask.** Promise.then выполняется раньше setTimeout. async/await не блокирует - отдаёт управление event loop. Single-thread + async I/O = идеально для серверов

Связанные темы

JavaScript - фундамент всей экосистемы веб-разработки:

  • CSS: от каскада до Grid — JavaScript управляет стилями через classList, style и CSS Custom Properties
  • HTML и семантическая разметка — JavaScript взаимодействует с HTML через DOM API (document.querySelector, createElement)

Вопросы для размышления

  • Откройте консоль браузера и выполните: `[] == ![]`. Результат - true. Можно ли объяснить, какие приведения типов к этому привели?
  • Напишите функцию `once(fn)`, которая разрешает вызвать fn только один раз, а при последующих вызовах возвращает первый результат. Какая концепция из этого урока - ключевая?
  • Представьте: Node.js сервер обрабатывает запрос с тяжёлым парсингом JSON (100 MB). Как будет вести себя event loop и что произойдёт с другими запросами?

Связанные уроки

  • web-02 — JavaScript манипулирует CSS и DOM из предыдущего урока
  • web-04 — React, TypeScript и современные фреймворки строятся поверх основ JS
  • web-05 — Async/await и Promises расширяют event loop из этого урока
  • se-05 — Event loop - паттерн Observer в disguise на уровне рантайма
  • alg-01 — Prototype chain lookup - линейный поиск, стоит понимать сложность
  • mob-03 — SwiftUI @State и React hooks - одна декларативная идея на разных платформах
  • comp-33-v8
JavaScript: основы языка

0

1

Войти