diff --git a/js/index.md b/js/index.md index b9780f3e9b..af9537be95 100644 --- a/js/index.md +++ b/js/index.md @@ -12,6 +12,7 @@ groups: - while - for - expressions-vs-statements + - operators - ref-type-vs-value-type - function - closures diff --git a/js/operators/index.md b/js/operators/index.md new file mode 100644 index 0000000000..784f5e02e3 --- /dev/null +++ b/js/operators/index.md @@ -0,0 +1,303 @@ +--- +title: "Базовые операторы в JS" +description: "Обзор ключевых операторов JavaScript: арифметика, приоритет, инкремент и декремент, присваивание." +authors: + - anton-fomichev +related: + - js/typecasting + - js/logic-operators + - js/ternary-operator +tags: + - doka +--- + +## Кратко + +В JavaScript есть несколько базовых операторов, с которыми разработчики сталкиваются постоянно в ходе своей работы. Они позволяют выполнять арифметические действия, изменять значения переменных, а также помогают разработчику упростить некоторые операции в коде. + +## Кто такие: «оператор», «операнд»? + +- **Оператор** — символ (или ключевое слово), говорящий движку, какую операцию совершить + +- **Операнд** — это то, над чем оператор выполняет действие (например: число, строка, переменная) + +## Типы операторов + +### Унарные операторы + +Унарные операторы зависят от одного операнда. Другими словами, они действуют только на один объект. + +Пример — _унарный минус_ `-`: + +```js +let x = 5 +console.log(-x) +// -5 +``` + +### Бинарные операторы + +Бинарные операторы применяются к двум операндам. + +Например, оператор `+` применяется к операндам `x` и `y`: + +```js +const x = 5, y = 2 +console.log(x + y) +// 7 +``` + +Рассмотрим другой пример: оператор `>` сравнивает числа и возвращает значение логического типа: + +```js +const user = { age: 19 } +console.log(user.age > 18) +// true +``` + +## Математика + +JavaScript поддерживает стандартные арифметические операторы, которые помогают складывать, вычитать, умножать и делить числа. Ниже перечислены самые популярные из них: + +### Сложение, вычитание, умножение и деление + +```js +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 +``` + +### Остаток от деления + +```js +console.log(10 % 3) +// 1 +console.log(8 % 2) +// 0 +``` + +### Возведение в степень + +```js +console.log(2 ** 2) +// 4 +console.log(2 ** 5) +// 32 +``` + +## Не только математика + +JavaScript позволяет применять операторы не только к числам — на практике можно встретить ситуации, когда один или оба операнда оказываются строками, булевыми значениями или даже объектами. + +
+Как выполнение операторов описывает спецификация ECMAScript + +Рассмотрим шаги, определённые в [спецификации ECMAScript](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-applystringornumericbinaryoperator) на примере оператора `+`: + +Сначала оба операнда последовательно приводятся к примитивам. Если хотя бы один из операндов — строка, то второй операнд будет приведён к строке и результатом операции будет конкатенация: + +```js +console.log(5 + '2') +// '52' +``` + +В противном случае операция рассматривается как арифметическая. JavaScript приводит оба операнда к числовому типу — Number или BigInt. Если один операнд оказался Number, а другой — BigInt, будет выброшена ошибка TypeError: + +```js +42n + 1 // TypeError: Cannot mix BigInt and other types, use explicit conversions +``` + +Если оба операнда — Number, используется операция _Number::add_. Если оба операнда оказываются BigInt, вызывается _BigInt:add_: + +```js +console.log(2 + 3) +// 5 +console.log(2n + 3n) +// 5n +``` + +
+ +### Конкатенация строк + +Если хотя бы один операнд у оператора `+` — строка, в результате получится склейка (конкатенация). Например: + +```js +console.log('Hello' + ' ' + 'world') +// 'Hello world' +console.log('5' + 2) +// '52' +``` + +### Сравнение строк + +Операторы сравнения, такие как `>` или `<`, могут применяться к строкам. При этом строки сравниваются _лексикографически_ — посимвольно в порядке символов, а не по их «числовому» содержанию. + +Следующее выражение возвращает `true` после сравнения первых символов строк `'2'` и `'15'` — `'2'` и `'1'`: + +```js +console.log('2' > '15') +// true +``` + +Каждому символу соответствует код из [UTF-16](https://ru.wikipedia.org/wiki/UTF-16), с помощью которого и происходит сравнение двух символов. + +### Преобразование нечисловых типов + +Во время выполнения арифметических операций JavaScript пытается преобразовать операнды к числу. Например, при использовании оператора `-` со строкой: + +```js +let x = '10' +console.log(x - 1) +// 9, строка '10' привелась к числу 10 +``` + +Также существует унарный плюс `+`, который явно превращает строку в число: + +```js +console.log(+'42') +// 42 +``` + +При попытке использовать арифметические операторы вроде `+`, `-` или `*` с объектами или массивами, JavaScript попытается привести их к примитиву через метод `toString()` или `valueOf()`. Иногда это приводит к результатам, которые сложно предсказать, если не знать механизма преобразований: + +```js +console.log({} + {}) +// '[object Object][object Object]' + +// В массиве по умолчанию toString() склеивает элементы: +console.log([1, 2, 3] + [4, 5]) +// '1,2,34,5' +``` + +В арифметическом контексте `true` приводится к `1`, а `false` — к `0`. Однако при конкатенации строк (`+`) булевые значения не будут автоматически превращаться в числа: + +```js +console.log(true + 1) +// 2 +console.log(false + 10) +// 10 +console.log(true + '1') +// 'true1' +``` + +Подробнее о том, как JavaScript преобразует строки, объекты и другие типы данных, можно посмотреть в статье [преобразование типов](/js/typecasting/). + +## Приоритет операторов + +В JavaScript у операторов есть определённый порядок выполнения. Это означает, что некоторые операции будут выполняться раньше других, если в выражении нет дополнительных скобок. + +Приоритет _унарных_ операторов выше, чем приоритет _бинарных_ (за некоторыми исключениями: оператор группировки, вызов функции, доступ к свойствам и т. п.). Подробнее можно посмотреть в [таблице приоритетов](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Operator_precedence#таблица). + +На практике запоминать приоритет всех операторов не нужно: в спорных случаях всегда лучше проставить скобки явно. + +### Присваивание + +Оператор присваивания `=` в JavaScript находится почти в самом низу приоритетов. Его суть проста: взять значение выражения справа и присвоить переменной слева. Например: + +```js +const result = 10 +``` + +Важно, что оператор присваивания возвращает значение, которое было присвоено переменной: + +```js +let a +console.log(a = 'Hello world!') +// 'Hello world!' +``` + +Оператор присваивания выполняется справа налево. В примере ниже, сначала выполнится `b = 5`, вернётся `5`, а затем значение присвоится `a`. В итоге `a` и `b` оба станут равны `5`: + +```js +let a, b +console.log(a = b = 5) +// 5 +console.log(a, b) +// 5 5 +``` + +#### Как делать не надо + +Иногда можно увидеть слишком хитрые конструкции, где в одном выражении смешиваются разные операторы, в том числе присваивания. Например: + +```js +const a = 2 + (b = 3 * 5) // a = 17, b = 15 +``` + +Такой код работает, но его сложно читать и поддерживать, особенно если в нём участвуют более сложные вычисления. В большинстве случаев лучше разбить логику на несколько строк — так понятнее и надёжнее: + +```js +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`. + +Зачем это нужно? + +1. **Короткая запись**. Вместо `x = x + 10` можно использовать `x += 10`; +1. **Удобство при изменении счётчика**. В циклах или при пошаговом изменении переменной такие записи упрощают код и делают его немного выразительнее. + +## Инкремент и декремент + +Инкремент и декремент — это унарные операторы, которые увеличивают или уменьшают значение переменной на `1`. Выглядят они так: + +- **Инкремент**: `++`, +- **Декремент**: `--`. + +При этом существует две формы — _префиксная_ и _постфиксная_. Они отличаются моментом, когда переменная меняет своё значение. + +### Постфиксная форма + +Постфиксные операторы (`x++`, `x--`) сначала **возвращают старое значение**, а лишь затем меняют переменную: + +```js +let x = 5 +console.log(x++) +// 5 (возвращаем старое значение) +console.log(x) +// 6 (теперь переменная увеличена) +``` + +### Префиксная форма + +Префиксные операторы (`++x`, `--x`) сначала изменяют переменную, а затем **возвращают новое значение**: + +```js +let count = 5 +console.log(++count) +// 6 (значение уже увеличено) +console.log(count) +// 6 (значение остаётся увеличенным) +``` + +### Когда использовать + +Инкремент и декремент нередко применяют в циклах `for` или `while`, чтобы удобнее управлять переменными-счётчиками. Вывод чисел от 0 до 4 (включительно) в консоль может выглядеть так: + +```js +for (let i = 0; i < 5; i++) { + console.log(i) +} +``` + +### Подводные камни + +Чаще всего проблемы в использовании инкремента или декремента в JavaScript связаны с особенностями постфиксной или префиксной формы. Если не помните, когда возвращается старое значение, а когда новое, лучше используйте отдельные инструкции присваивания. diff --git a/people/anton-fomichev/index.md b/people/anton-fomichev/index.md new file mode 100644 index 0000000000..cbb03b30f2 --- /dev/null +++ b/people/anton-fomichev/index.md @@ -0,0 +1,7 @@ +--- +name: 'Антон Фомичев' +url: https://github.com/anton-fomichev +photo: photo.jpeg +--- + +Разрабатываю и оптимизирую фронтенд с 2021 года. Уделяю особое внимание UX. Для меня важно не стоять на месте: экспериментирую с технологиями, делюсь знаниями в open source сообществе 🤝 diff --git a/people/anton-fomichev/photo.jpeg b/people/anton-fomichev/photo.jpeg new file mode 100644 index 0000000000..69e63c6ae7 Binary files /dev/null and b/people/anton-fomichev/photo.jpeg differ