Логическое присваивание (&&=, ||=, ??=)

17 июля 2026
Автор

Антон Ларичев

Что такое операторы логического присваивания

Операторы логического присваивания появились в JavaScript в стандарте ES2021. Они объединяют логическую операцию и присваивание в одном выражении и позволяют писать компактный, читаемый код при работе с условными значениями.

Всего таких операторов три:

  • ||= — присваивание через логическое ИЛИ
  • &&= — присваивание через логическое И
  • ??= — присваивание через оператор нулевого слияния

Каждый из них работает по принципу «ленивого» вычисления: правая часть выражения выполняется только тогда, когда это необходимо.

Оператор ||= (OR assignment)

Синтаксис и принцип работы

Оператор ||= присваивает значение переменной только в том случае, если текущее значение является ложным (falsy).

x ||= y;
// Эквивалентно:
x || (x = y);

Обратите внимание: запись x ||= y не равнозначна x = x || y. Разница в том, что при x ||= y присваивание вообще не происходит, если x уже является истинным — это важно в случаях, когда установка значения вызывает побочные эффекты (например, сеттер в объекте).

Пример использования

Типичная задача: задать значение по умолчанию, если переменная пуста.

let username = '';
username ||= 'Гость';
console.log(username); // 'Гость'

let city = 'Москва';
city ||= 'Не указан';
console.log(city); // 'Москва'

Это удобная замена паттерна if (!x) x = default:

// Было:
if (!config.timeout) {
  config.timeout = 3000;
}

// Стало:
config.timeout ||= 3000;

Что считается falsy

Оператор ||= срабатывает для всех ложных значений JavaScript:

let a = null;
let b = undefined;
let c = 0;
let d = '';
let e = false;
let f = NaN;

a ||= 'новое'; // 'новое'
b ||= 'новое'; // 'новое'
c ||= 'новое'; // 'новое'
d ||= 'новое'; // 'новое'
e ||= 'новое'; // 'новое'
f ||= 'новое'; // 'новое'

Это означает, что ||= заменит 0, пустую строку и false — что иногда нежелательно.

Оператор &&= (AND assignment)

Синтаксис и принцип работы

Оператор &&= присваивает новое значение только в том случае, если текущее значение является истинным (truthy).

x &&= y;
// Эквивалентно:
x && (x = y);

То есть: «если x существует и истинно — обнови его».

Пример использования

let user = { name: 'Иван', role: 'admin' };
user &&= { ...user, verified: true };
console.log(user); // { name: 'Иван', role: 'admin', verified: true }

let guest = null;
guest &&= { ...guest, verified: true };
console.log(guest); // null — присваивание не произошло

Практический пример — обновление вложенных данных без проверки существования:

// Было:
if (element.dataset) {
  element.dataset.loaded = 'true';
}

// Стало:
element.dataset &&= { ...element.dataset, loaded: 'true' };

Ещё один распространённый случай — мутирование объекта только при его наличии:

const cache = new Map();

function updateCache(key, value) {
  // Обновляем запись только если она уже есть в кеше
  cache.get(key) &&= value;
}

Работа с DOM

&&= хорошо подходит для обновления DOM-элементов, которые могут отсутствовать:

const tooltip = document.querySelector('.tooltip');
// Обновляем текст подсказки только если элемент существует
tooltip &&= Object.assign(tooltip, { textContent: 'Новый текст' });

Оператор ??= (Nullish Coalescing Assignment)

Синтаксис и принцип работы

Оператор ??= присваивает значение только в том случае, если текущее значение равно null или undefined (nullish-значения).

x ??= y;
// Эквивалентно:
x ?? (x = y);

Это ключевое отличие от ||=: оператор ??= не затрагивает 0, пустую строку и false.

Пример использования

let score = 0;
score ??= 100;
console.log(score); // 0 — не перезаписано, так как 0 не является null/undefined

let title = '';
title ??= 'Без названия';
console.log(title); // '' — не перезаписано

let value = null;
value ??= 'по умолчанию';
console.log(value); // 'по умолчанию'

let missing = undefined;
missing ??= 42;
console.log(missing); // 42

Инициализация настроек

??= идеально подходит для задания начальных значений конфигурации, где 0 и false являются допустимыми значениями:

function createServer(options = {}) {
  options.port ??= 3000;
  options.host ??= 'localhost';
  options.debug ??= false;
  options.maxConnections ??= 100;

  return options;
}

console.log(createServer({ port: 8080, debug: false }));
// { port: 8080, host: 'localhost', debug: false, maxConnections: 100 }
// debug: false НЕ перезаписан на false по умолчанию — и это правильно

Сравнение трёх операторов

Чтобы наглядно увидеть разницу между операторами, рассмотрим одни и те же значения:

const values = [null, undefined, 0, '', false, NaN, 'данные', 42, true];

for (const val of values) {
  let a = val;
  let b = val;
  let c = val;

  a ||= 'заменено';
  b &&= 'заменено';
  c ??= 'заменено';

  console.log(`${String(val).padEnd(10)} ||= ${String(a).padEnd(10)} &&= ${String(b).padEnd(10)} ??= ${c}`);
}

Результат:

null       ||= заменено   &&= null       ??= заменено
undefined  ||= заменено   &&= undefined  ??= заменено
0          ||= заменено   &&= 0          ??= 0
           ||= заменено   &&=            ??= 
false      ||= заменено   &&= false      ??= false
NaN        ||= заменено   &&= NaN        ??= NaN
данные     ||= данные     &&= заменено   ??= данные
42         ||= 42         &&= заменено   ??= 42
true       ||= true       &&= заменено   ??= true

Таблица выбора оператора:

Ситуация Оператор
Заменить любое falsy значение ||=
Обновить существующее (truthy) значение &&=
Заменить только null/undefined ??=

Практические сценарии

Инициализация состояния компонента

class Component {
  constructor(props) {
    this.state = {};

    // Безопасная инициализация: не трогаем false и 0
    this.state.count ??= props.initialCount ?? 0;
    this.state.visible ??= props.visible ?? true;
    this.state.label ??= props.label ?? '';
  }
}

Кеширование вычислений

const memo = {};

function computeExpensive(key) {
  // Вычисляем только если значения нет в кеше
  memo[key] ??= heavyComputation(key);
  return memo[key];
}

function heavyComputation(key) {
  console.log(`Вычисляю для ${key}...`);
  return key.toUpperCase();
}

computeExpensive('hello'); // Вычисляю для hello...
computeExpensive('hello'); // Берёт из кеша, вывода нет

Накопление данных

function groupBy(array, key) {
  return array.reduce((groups, item) => {
    // Создаём массив для группы только при первом появлении ключа
    groups[item[key]] ??= [];
    groups[item[key]].push(item);
    return groups;
  }, {});
}

const users = [
  { name: 'Алиса', role: 'admin' },
  { name: 'Боб', role: 'user' },
  { name: 'Виктор', role: 'admin' },
  { name: 'Галина', role: 'user' },
];

console.log(groupBy(users, 'role'));
// {
//   admin: [{ name: 'Алиса', role: 'admin' }, { name: 'Виктор', role: 'admin' }],
//   user:  [{ name: 'Боб', role: 'user' }, { name: 'Галина', role: 'user' }]
// }

Обновление конфигурации

const defaults = { theme: 'light', lang: 'ru', notifications: true };

function applyUserPreferences(config, userPrefs) {
  // &&= применяем только если userPrefs существует
  // ??= для каждого поля — берём из defaults только если не задано
  if (userPrefs) {
    for (const key of Object.keys(defaults)) {
      config[key] ??= userPrefs[key] ?? defaults[key];
    }
  } else {
    for (const key of Object.keys(defaults)) {
      config[key] ??= defaults[key];
    }
  }
  return config;
}

const result = applyUserPreferences(
  { theme: 'dark' },
  { lang: 'en', notifications: false }
);
// { theme: 'dark', lang: 'en', notifications: false }

Ленивое вычисление правой части

Важная особенность всех трёх операторов — правая часть не вычисляется, если присваивание не нужно. Это отличает их от записи вида x = x || y:

let counter = 0;

function sideEffect() {
  counter++;
  return 'значение';
}

let a = 'существующее';
a ||= sideEffect(); // sideEffect не вызывается, counter остаётся 0
console.log(counter); // 0

let b = null;
b ??= sideEffect(); // sideEffect вызывается, counter становится 1
console.log(counter); // 1

Это полезно, когда правая часть — дорогостоящая операция или функция с побочными эффектами.

Чего не стоит делать

Путать ||= и ??=

// Ошибка: счётчик visits может быть 0, но его перезапишут
function trackVisit(stats) {
  stats.visits ||= 0; // Если visits === 0, всё равно присвоит 0, но логика неверна
}

// Правильно:
function trackVisit(stats) {
  stats.visits ??= 0; // Присвоит только если visits не задан
}

Злоупотреблять &&= с объектами

// Плохо: труднее читать, чем явная проверка
obj.nested &&= obj.nested.value &&= transform(obj.nested.value);

// Лучше: использовать опциональную цепочку
if (obj.nested?.value) {
  obj.nested.value = transform(obj.nested.value);
}

Поддержка в браузерах и окружениях

Операторы логического присваивания доступны во всех современных браузерах и Node.js начиная с версии 15:

  • Chrome 85+
  • Firefox 79+
  • Safari 14+
  • Node.js 15+

При работе со старыми окружениями используйте Babel с плагином @babel/plugin-proposal-logical-assignment-operators или TypeScript, который компилирует их в совместимый код автоматически начиная с версии 4.0.

Итог

Операторы логического присваивания позволяют писать лаконичный код для распространённых паттернов:

  • ||= — присвоить значение по умолчанию, если переменная содержит любое falsy значение
  • &&= — обновить переменную только если она уже содержит truthy значение
  • ??= — присвоить значение по умолчанию только при null или undefined, не трогая 0, false и пустую строку

Главное правило выбора: если нужно сохранить 0 и false как осмысленные значения — используйте ??=, а не ||=.

Для углублённого изучения JavaScript и современного синтаксиса пройдите курс на PurpleSchool: https://purpleschool.ru/course/javascript?utm_source=knowledgebase&utm_medium=text&utm_campaign=logical-assignment-operators

Стрелочка влевоОператор нулевого слияния (??) в JavaScript

Постройте личный план изучения Javascript до уровня Middle — бесплатно!

Javascript — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Javascript

Как работает метод trim() - JavaScriptКак работает метод toUpperCase() - JavaScriptКак работает метод toLowerCase() - JavaScriptКак работает метод substring() - JavaScriptКак работает метод startsWith() - JavaScriptКак работает метод split() - JavaScriptКак работает метод slice() - JavaScriptКак работает метод search() - JavaScriptКак работает метод replaceAll() - JavaScriptКак работает метод replace() - JavaScriptКак работает метод repeat() - JavaScriptКак работает метод padStart() - JavaScriptКак работает метод padEnd() - JavaScriptКак работает метод matchAll() - JavaScriptКак работает метод match() - JavaScriptКак работает метод localeCompare() - JavaScriptКак работает свойство length - JavaScriptКак работает метод lastIndexOf() - JavaScriptКак работает метод indexOf() - JavaScriptКак работает метод includes() - JavaScriptКак работает метод fromCodePoint() - JavaScriptКак работает метод fromCharCode() - JavaScriptКак работает метод endsWith() - JavaScriptКак работает метод concat() - JavaScriptКак работает метод codePointAt() - JavaScriptКак работает метод charCodeAt() - JavaScriptКак работает метод charAt() - JavaScript
Итератор в JavaScript
try...catch в JavaScriptError в JavaScript
Событие wheel в JavaScriptСобытие unload в JavaScriptСобытие touch в JavaScriptСобытие submit в JavaScriptСобытие scroll в JavaScriptСобытие reset в JavaScriptМетод .preventDefault() в JavaScriptСобытие mouseover в JavaScriptСобытие mouseout в JavaScriptСобытие load в JavaScriptСобытие keyup в JavaScriptСобытие keydown в JavaScriptСобытие invalid в JavaScriptСобытийная модель Event в JavaScriptОбъект события Event в JavaScriptСобытие DOMContentLoaded в JavaScriptСобытие dblclick в JavaScriptСобытие input в JavaScriptСобытие click в JavaScriptСобытие change в JavaScriptСобытие beforeunload в JavaScript
Как работает метод some() - JavaScriptКак работает метод reverse() - JavaScriptКак работает метод reduce() - JavaScriptКак работает метод map() - JavaScriptКак работает метод isArray() - JavaScriptКак работает метод indexOf() - JavaScriptКак работает метод includes() - JavaScriptКак работает метод from() - JavaScriptКак работает метод forEach() - JavaScriptКак работает метод flatMap() - JavaScriptКак работает метод flat() - JavaScriptКак работает метод findIndex() - JavaScriptКак работает метод find() - JavaScriptКак работает метод filter() - JavaScriptКак работает метод every() - JavaScriptМассивы в JavaScriptArray.at, findLast, findLastIndex — новые методы массивов
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы JavaScript

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий