JavaScript

JavaScript — динамический язык, который в браузере управляет документом, событиями и сетевыми запросами. Его модель заметно отличается от Java: тип переменной определяется значением во время выполнения, функции являются объектами, а асинхронное продолжение обычно проходит через event loop.

Объявление

const userId = 42;
let status = "loading";
 
status = "ready";
// userId = 43; // нельзя переназначить const

const запрещает переназначение binding, но не делает объект immutable:

const settings = { theme: "light" };
settings.theme = "dark"; // допустимо

var имеет function scope и hoisting-поведение, которое проще случайно понять неправильно. Для нового кода обычно достаточно const и let.

Типы

Примитивы:

  • string;
  • number;
  • bigint;
  • boolean;
  • undefined;
  • symbol;
  • null как отдельное значение, хотя typeof null исторически возвращает "object".

Объекты включают arrays, functions, dates и обычные records.

typeof 10;          // "number"
typeof "10";        // "string"
Array.isArray([]);  // true

Сравнение

=== сравнивает без неявного преобразования типов. == может преобразовать операнды:

0 == false;   // true
0 === false;  // false

Для предсказуемого кода использую strict equality.

Функции и closures

Функция запоминает lexical environment, где была создана:

function counter() {
  let value = 0;
  return () => ++value;
}
 
const next = counter();
next(); // 1
next(); // 2

Closure позволяет хранить состояние, но также способен удерживать объекты в памяти дольше ожидаемого.

Event loop

JavaScript-код в браузерном main thread выполняет один текущий call stack. Асинхронная операция не «продолжает функцию сама»: после завершения её callback ставится в очередь.

Promise callbacks относятся к microtasks и выполняются после текущего stack, до следующей обычной task:

console.log("A");
 
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
 
console.log("D");
// A, D, C, B

Долгое синхронное вычисление блокирует обработку событий и отрисовку, даже если вокруг приложения много async функций.

Promise и async/await

async function loadItem(id) {
  const response = await fetch(`/api/items/${id}`);
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.json();
}

await приостанавливает конкретную async function, но не весь thread. Ошибка Promise превращается в исключение в точке await.

Подробнее: AJAX fetch.

Модули

// math.js
export function sum(left, right) {
  return left + right;
}
 
// app.js
import { sum } from "./math.js";

ES modules имеют собственную область видимости и статические imports, которые анализируются до исполнения.

Вопросы для собеседования

Чем undefined отличается от null?

undefined обычно означает отсутствие присвоенного значения. null — явно записанное отсутствие.

Что хранит closure?

Доступ к lexical environment, а не снимок отдельных значений.

Почему fetch с HTTP 500 не всегда попадает в catch?

Promise отклоняется при сетевой ошибке, но HTTP error остаётся нормальным response. Нужно проверить response.ok.

Делает ли async функцию параллельной?

Нет. Она возвращает Promise и позволяет неблокирующее ожидание, но CPU-код всё равно выполняется в thread, пока не вынесен в worker.

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