Кратко
СкопированоВ JavaScript есть несколько базовых операторов, с которыми разработчики сталкиваются постоянно в ходе своей работы. Они позволяют выполнять арифметические действия, изменять значения переменных, а также помогают упростить некоторые операции в коде.
Кто такие «оператор» и «операнд»?
СкопированоВ выражениях встречаются два ключевых элемента:
- оператор — символ или ключевое слово, говорящее движку, какую операцию совершить;
- операнд — это то, над чем оператор выполняет действие.
В примере ниже плюс — оператор, а два числа — операнды.
console.log(2 + 2)// 4
console.log(2 + 2)
// 4
Типы операторов
СкопированоУнарные операторы
СкопированоУнарные операторы зависят от одного операнда. Другими словами, они действуют только на один объект. Пример — унарный минус -:
let x = 5console.log(-x)// -5
let x = 5
console.log(-x)
// -5
Бинарные операторы
СкопированоБинарные операторы применяются к двум операндам. Например, оператор + применяется к операндам x и y:
const x = 5, y = 2console.log(x + y)// 7
const x = 5, y = 2
console.log(x + y)
// 7
Рассмотрим другой пример: оператор > сравнивает числа и возвращает значение логического типа:
const user = { age: 19 }console.log(user.age > 18)// true
const user = { age: 19 }
console.log(user.age > 18)
// true
Математика
СкопированоJavaScript поддерживает стандартные арифметические операторы, которые помогают складывать, вычитать, умножать и делить числа. Ниже перечислены самые популярные из них:
Сложение, вычитание, умножение и деление
Скопировано
console.log(10 + 3)// 13console.log(10 - 3)// 7console.log(10 * 3)// 30console.log(10 / 3)// 3.3333...console.log(10 - 'blablabla')// NaN
console.log(10 + 3)
// 13
console.log(10 - 3)
// 7
console.log(10 * 3)
// 30
console.log(10 / 3)
// 3.3333...
console.log(10 - 'blablabla')
// NaN
Остаток от деления
Скопировано
console.log(10 % 3)// 1console.log(8 % 2)// 0
console.log(10 % 3)
// 1
console.log(8 % 2)
// 0
Возведение в степень
Скопировано
console.log(2 ** 2)// 4console.log(2 ** 5)// 32
console.log(2 ** 2)
// 4
console.log(2 ** 5)
// 32
Не только математика
СкопированоJavaScript позволяет применять операторы не только к числам — на практике можно встретить ситуации, когда один или оба операнда оказываются строками, булевыми значениями или даже объектами.
Как выполнение операторов описывает спецификация ECMAScript
Рассмотрим шаги, определённые в спецификации ECMAScript на примере оператора +:
Сначала оба операнда последовательно приводятся к примитивам. Если хотя бы один из операндов — строка, то второй операнд будет приведён к строке и результатом операции будет конкатенация:
console.log(5 + '2')// '52'
console.log(5 + '2')
// '52'
В противном случае операция рассматривается как арифметическая. JavaScript приводит оба операнда к числовому типу — Number или BigInt. Если один операнд оказался Number, а другой — BigInt, будет выброшена ошибка TypeError:
42n + 1 // TypeError: Cannot mix BigInt and other types, use explicit conversions
42n + 1 // TypeError: Cannot mix BigInt and other types, use explicit conversions
Если оба операнда — Number, используется операция Number::add. Если оба операнда оказываются BigInt, вызывается BigInt:add:
console.log(2 + 3)// 5console.log(2n + 3n)// 5n
console.log(2 + 3)
// 5
console.log(2n + 3n)
// 5n
Конкатенация строк
СкопированоЕсли хотя бы один операнд у оператора + — строка, в результате получится склейка (конкатенация). Например:
console.log('Hello' + ' ' + 'world')// 'Hello world'console.log('5' + 2)// '52'
console.log('Hello' + ' ' + 'world')
// 'Hello world'
console.log('5' + 2)
// '52'
Сравнение строк
СкопированоОператоры сравнения, такие как > или <, могут применяться к строкам. При этом строки сравниваются лексикографически — посимвольно в порядке символов, а не по их «числовому» содержанию.
Следующее выражение возвращает true после сравнения первых символов строк '2' и '15' — '2' и '1':
console.log('2' > '15')// true
console.log('2' > '15')
// true
Каждому символу соответствует код из UTF-16, с помощью которого и происходит сравнение двух символов.
Преобразование нечисловых типов
СкопированоВо время выполнения арифметических операций JavaScript пытается преобразовать операнды к числу. Например, при использовании оператора - со строкой:
let x = '10'console.log(x - 1)// 9, строка '10' привелась к числу 10
let x = '10'
console.log(x - 1)
// 9, строка '10' привелась к числу 10
Также существует унарный плюс +, который явно превращает строку в число:
console.log(+'42')// 42
console.log(+'42')
// 42
При попытке использовать арифметические операторы вроде +, - или * с объектами или массивами, JavaScript попытается привести их к примитиву через метод to или value. Иногда это приводит к результатам, которые сложно предсказать, если не знать механизма преобразований:
console.log({} + {})// '[object Object][object Object]'// В массиве по умолчанию toString() склеивает элементы:console.log([1, 2, 3] + [4, 5])// '1,2,34,5'
console.log({} + {})
// '[object Object][object Object]'
// В массиве по умолчанию toString() склеивает элементы:
console.log([1, 2, 3] + [4, 5])
// '1,2,34,5'
В арифметическом контексте true приводится к 1, а false — к 0. Однако, при конкатенации строк (+) булевые значения не будут автоматически превращаться в числа:
console.log(true + 1)// 2console.log(false + 10)// 10console.log(true + '1')// 'true1'
console.log(true + 1)
// 2
console.log(false + 10)
// 10
console.log(true + '1')
// 'true1'
Подробнее о том, как JavaScript преобразует строки, объекты и другие типы данных, можно посмотреть в статье преобразование типов.
Приоритет операторов
СкопированоВ JavaScript у операторов есть определённый порядок выполнения. Это означает, что некоторые операции будут выполняться раньше других, если в выражении нет дополнительных скобок.
Приоритет унарных операторов выше, чем приоритет бинарных (за некоторыми исключениями: оператор группировки, вызов функции, доступ к свойствам и т. п.). Подробнее можно посмотреть в таблице приоритетов.
На практике запоминать приоритет всех операторов не нужно: в спорных случаях всегда лучше проставить скобки явно.
Присваивание
СкопированоОператор присваивания = в JavaScript находится почти в самом низу приоритетов. Его суть проста: взять значение выражения справа и присвоить переменной слева. Например:
const result = 10
const result = 10
Важно, что оператор присваивания возвращает значение, которое присвоили переменной:
let aconsole.log(a = 'Hello world!')// 'Hello world!'
let a
console.log(a = 'Hello world!')
// 'Hello world!'
Оператор присваивания выполняется справа налево. В примере ниже, сначала выполнится b , вернётся 5, а затем значение присвоится a. В итоге a и b оба станут равны 5:
let a, bconsole.log(a = b = 5)// 5console.log(a, b)// 5 5
let a, b
console.log(a = b = 5)
// 5
console.log(a, b)
// 5 5
Как делать не надо
СкопированоИногда можно увидеть слишком хитрые конструкции, где в одном выражении смешиваются разные операторы, в том числе присваивания. Например:
const a = 2 + (b = 3 * 5) // a = 17, b = 15
const a = 2 + (b = 3 * 5) // a = 17, b = 15
Такой код работает, но его сложно читать и поддерживать, особенно если в нём участвуют более сложные вычисления. В большинстве случаев лучше разбить логику на несколько строк — так понятнее и надёжнее:
const b = 3 * 5 // 15const a = 2 + b // 17
const b = 3 * 5 // 15
const a = 2 + b // 17
Инкрементное присваивание
СкопированоПомимо обычного оператора =, в JavaScript есть целая группа сокращённых операторов присваивания. Они позволяют одновременно выполнить арифметическую операцию и присвоить результат переменной. Например:
x +эквивалентно= 1 x;= x + 1 xэквивалентно- = 2 x;= x - 2 x *эквивалентно= 3 x;= x* 3 xэквивалентно/ = 4 x;= x / 4 xэквивалентно% = 5 x;= x % 5 x **эквивалентно= 6 x.= x ** 6
Зачем это нужно?
- Короткая запись. Вместо
xможно использовать= x + 10 x +.= 10 - Удобство при изменении счётчика. В циклах или при пошаговом изменении переменной такие записи упрощают код и делают его немного выразительнее.
Инкремент и декремент
СкопированоИнкремент и декремент — это унарные операторы, которые увеличивают или уменьшают значение переменной на 1. Выглядят они так:
- инкремент:
++, - декремент:
-.-
При этом существует две формы — префиксная и постфиксная. Они отличаются моментом, когда переменная меняет своё значение.
Постфиксная форма
СкопированоПостфиксные операторы (x++, x) сначала возвращают старое значение, а лишь затем меняют переменную:
let x = 5console.log(x++)// 5 (возвращаем старое значение)console.log(x)// 6 (теперь переменная увеличена)
let x = 5
console.log(x++)
// 5 (возвращаем старое значение)
console.log(x)
// 6 (теперь переменная увеличена)
Префиксная форма
СкопированоПрефиксные операторы (++x, -) сначала изменяют переменную, а затем возвращают новое значение:
let count = 5console.log(++count)// 6 (значение уже увеличено)console.log(count)// 6 (значение остаётся увеличенным)
let count = 5
console.log(++count)
// 6 (значение уже увеличено)
console.log(count)
// 6 (значение остаётся увеличенным)
Когда использовать
СкопированоИнкремент и декремент нередко применяют в циклах for или while, чтобы удобнее управлять переменными-счётчиками. Вывод чисел от 0 до 4 (включительно) в консоль может выглядеть так:
for (let i = 0; i < 5; i++) { console.log(i)}
for (let i = 0; i < 5; i++) {
console.log(i)
}
Подводные камни
СкопированоЧаще всего проблемы в использовании инкремента или декремента в JavaScript связаны с особенностями постфиксной или префиксной формы. Если не помните, когда возвращается старое значение, а когда новое, лучше используйте отдельные инструкции присваивания.