Js деление по модулю. Операции в JavaScript. Выражения и операторы в JavaScript

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

Сложение (+)
Оператор «плюс» складывает числовые операнды или выполняет конкатенацию строк. Если одним из операндов является строка, другой операнд преобразуется в строку и выполняется конкатенация. Операнды объекты преобразуются в числа или строки, которые могут быть сложены или конкатенированы. Преобразование выполняется с помощью методов valueOf() и/или toString().

Вычитание (−)
Когда «минус» используется в качестве бинарного оператора, он выполняет вычитание второго операнда из первого. Если указаны нечисловые операнды, то он пытается преобразовать их в числа.

Умножение (*)
Оператор * умножает два своих операнда. Нечисловые операнды он пытается преобразовать в числа.

Деление (/)
Оператор / делит первый операнд на второй. Пытается преобразовать нечисловые операнды в числа. Те, кто привык к языкам программирования, различающим целые и вещественные числа, могут ожидать получения целочисленного результата при делении одного целого на другое.

Однако в JavaScript все числа вещественные, поэтому все деления дают результат с плавающей точкой. Операция 5/2 дает 2.5, а не 2. Деление на ноль дает в качестве результата плюс или минут бесконечность, а 0/0 дает NaN.

Деление по модулю (%)
Оператор % вычисляет остаток, получаемый при целочисленном делении первого операнда на второй. Если заданы нечисловые операнды, то оператор пытается преобразовать их в числа. Знак результата совпадает со знаком первого операнда. Например, 5 % 2 дает 1.
Оператор деления по модулю обычно применятся с целыми операндами, но работает и для вещественных значений. Например, -4.3 % 2.1 дает результат -0.1.

Унарный минус (−)
Когда минус используется в качестве унарного оператора перед одиночным операндом, он выполняет унарную операцию смены знака. Другими словами, он преобразует положительное значение в отрицательное и наоборот. Если операнд не является числом, этот оператор пытается преобразовать его в число.

Унарный плюс (+)
Для симметрии с оператором «унарный минус» в JavaScript также имеется оператор унарный плюс. При помощи этого оператора можно явно задавать знак числовых литералов, если вы считаете, что это сделает текст программы более понятным:
var profit = +1000000;

В таком коде оператор «плюс» ничего не делает; результатом его работы является значение его аргумента.

Однако нечисловые аргументы он преобразует в числа. Если аргумент не может быть преобразован, возвращается NaN.

Инкремент (++)
Этот оператор инкрементирует (то есть увеличивает на единицу) свой единственный операнд, который должен быть переменной, элементом массива или свойством объекта. Если значение этой переменной, элемента массива или свойства не является числом, оператор сначала пытается преобразовать его в число. Точное поведение этого оператора зависит от его положения по отношению к операнду. Если поставить его перед операндом (префиксный оператор инкремента), то к операнду прибавляется 1, а результатом является увеличенное значение операнда. Если же он размещен после операнда (постфиксный оператор инкремента), то к операнду прибавляется 1, однако результатом является первоначальное значение операнда. Если увеличиваемое значение не является числом, оно в процессе вычисления преобразуется в число.

Например, следующий код устанавливает переменные i и j равными 2:
i = 1;
j = ++i;
А этот устанавливает i в 2, а j в 1:
i = 1;
j = i++;

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

Заметьте, что нельзя вставлять перевод строки между префиксным или постфиксным оператором инкремента и предшествующим ему операндом, поскольку точки с запятой в JavaScript вставляются автоматически. Если это сделать, JavaScript будет рассматривать операнд как полноценную инструкцию и вставит после него точку с запятой.

Декремент (−−)
Этот оператор декрементирует (то есть уменьшает на 1) свой единственный числовой операнд, который может представлять собой переменную, элемент массива или свойство объекта. Если значение этой переменной, элемента или свойства не является числом, оператор сначала пытается преобразовать его в число. Как и для оператора ++, точное поведение оператора -зависит от его положения относительно операнда. Будучи поставленным перед операндом, он уменьшает операнд и возвращает уменьшенное значение. После операнда он уменьшает операнд, но возвращает первоначальное значение.

Вы знакомы с арифметикой? Умеете складывать и вычитать числа? JavaScript тоже умеет складывать и вычитать. В JavaScript есть такие операторы: сложения +, вычитание -, деление /, умножение *, деление с возвратом остатка %.

Var one = 121;
var two = 13;
alert(one + two);
alert(one - two);
alert(one * two);
alert(one / two);
alert(one % two);

Усложним пример 1

Var answer = ((121 - 12) * 103) + 200;
alert(answer);

Усложним пример 2

Var PI = 3.14;

alert(answer);

И третий пример усложним

Var PI = 3.14;
var answer = ((121 - 12) * 103) + PI;
var answerEnd = (2 * answer * PI) + (-100);
alert(answerEnd);

И даже так можно сделать

Var answer = 101 + (-(-100));
alert(answer);

Но это ерунда, по сравнению с тем, что еще умеет JavaScript. Для этого есть стандартный объект Math с множеством свойств (свойства и есть операции, в данном контексте).

Math.pow(2, 53) /* -> 2 в степени 53 */
Math.round(0.6) /* -> 1.0 - округление до ближайшего целого */
Math.ceil(0.6) /* -> 1.0 - округление вверх */
Math.floor(0.6) /* -> 0.0 - округление вниз */
Math.abs(-5) /* -> 5 - модуль, абсолютное значение */
Math.max(x, y, z) /* -> Возвращает наибольший аргумент */
Math.min(x, y, z) /* -> Возвращает наименьший аргумент */
Math.random(); /* -> Где число на выходи больше 0, но меньше 1 */
Math.PI /* -> Число Пи */
Math.E /* -> Основание натурального логарифма */
Math.sqrt(3) /* -> Корень квадратный из 3 */
Math.pow(3, 1/3) /* -> Корень кубический из 3 */
Math.sin(0) /* -> Тригонометрия: имеются так же Math.cos, Math.atan и другие */
Math.log(10) /* -> Натуральный логарифм 10 */
Math.log(100) / Math.LN10 /* -> Логарифм 100 по основанию 10 */
Math.log(512) / Math.LN2 /* -> Логарифм 512 по основанию 2 */
Math.exp(3) /* -> Math.E в кубе */

Как использовать объект Math?

/* Первый пример */
var twoInPow = Math.pow(2, 53);
alert(twoInPow);
/* Второй пример */
var valueRaund = 0.1312;
var answerRaunt = Math.round(valueRaund);
alert(answerRaunt);
/* Третий пример */
var valueRaund = 0.1312;
alert(Math.round(valueRaund));
/* Четвертый пример: поиск наибольшего из трех чисел */
var a = 12, b = 11, c = 10;
alert(Math.max(a, b, c));

В случае выхода из диапазона, потери значащих разрядов или деления на ноль, JavaScript не выдает ошибку. Если результат будет слишком большой и выйдет из диапазона, то будет возвращено специальное значение «бесконечность», выглядит оно так « Infinity ».

Потеря значащих разрядов: результат арифметической операции оказывается очень близким к нулю. Если все же потеря была, то будет возвращен 0 (ноль).

Глобальная переменная NaN означает «не число». Есть одна особенность у этой переменной, операция проверки на равенство (==) всегда возвращает отрицательный результат и даже если его сравнивать с самим собой.

/* Так писать нельзя */
if (x == NaN) { ... }

Чтобы определить, является ли значение переменной x значением NaN, нужно использовать конструкцию, которая ниже. Эта проверка будет иметь значение true только тогда, когда x будет равен NaN

Если вы новичок в JavaScript, то такой жаргон как "modle bundlers vs. module loaders", "Webpack vs. Browserify" и "AMD vs. CommonJS" может поставить вас в тупик.

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

В этой статье я постараюсь объяснить всё простыми словами (с несколькими примерами кода). Надеюсь, что для вас эта статья окажется полезной.

Примечание: Для удобства статья будет разделена на две части. В первой части мы рассмотрим, что такое модули и почему мы их используем. Во второй части мы рассмотрим различные способы объединения модулей в единое целое.

Кто-нибудь объясните, что такое модули ещё раз?

Также как и главы книги, модули это просто кластеры слов (или кода, в зависимости от обстоятельств).

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

Зачем вообще использовать модули?

На самом деле у модулей есть много преимуществ. Наиболее важными, на мой взгляд, являются следующие:

  1. Удобная поддержка (Maintainability): По определению, модуль является самодостаточным. Хорошо спроектированный модуль призван уменьшить зависимости частей вашей кодовой базы насколько это возможно, чтобы она могла расти и совершенствоваться не зависимо друг от друга. Обновить один модуль гораздо проще, когда он отделён от других частей кода.Возвращаясь к нашей книге, например, если вы захотите внести небольшое изменение в одну главу и это повлечёт за собой изменения какого-то другого раздела вашей книги, это будет кошмар. Поэтому главу нужно писать так, чтобы при внесении правок, не пришлось затрагивать другие главы.
  2. Пространства имён (Namespacing): В JavaScript переменные которые находятся за пределами функций верхнего уровня считаются глобальными (каждый может получить к ним доступ). Поэтому очень распространено "Загрязнение пространства имён (namespace pollution)", где совершенно не связанный между собой код, связывают глобальные переменные.Совместное использование глобальных переменных в коде, который между собой не связан очень плохо в разработке .Дальше в этой статье мы увидим, что модули позволяют избежать загрязнения глобального пространства имён, путём создания приватных пространств для наших переменных.
  3. Повторное использование (Reusability): Давайте будем честными. Все мы копировали код в новые проекты, который писали раньше. Например, давайте представим, что вы скопировали в новый проект некоторые вспомогательные методы из предыдущего проекта.Хорошо, но если вы найдете наиболее хороший способ написать эту часть, вам придётся вспомнить все места где фигурировал этот код чтобы обновить его.Это безусловно огромная трата времени. Намного проще было бы написать модуль и использовать его повторно снова и снова.

Как можно интегрировать модули?

Есть много способов интегрировать модули в свои программы. Давайте рассмотрим некоторые из них:

Паттерн "Модуль"

Паттерн "Модуль" используется для имитации концепции классов (так как изначально JavaScript не поддерживал классы), поэтому мы можем хранить публичные и приватные методы (переменные) внутри одного объекта так, как делается это в классах других языков, таких как Java или Python. Это позволяет нам создать публичный API и предоставить возможность обращаться к публичным методам, в то время как приватные переменные и методы инкапсулированы в замыкании.

Есть несколько способов реализации паттерна "Модуль". В первом примере я буду использовать анонимные замыкания. Помещение кода в анонимную функцию поможет нам достичь цели. (Помните, что в JavaScript функции - это единственный способ чтобы создать новую область видимости).

Пример 1: Анонимные замыкания

(function () { // We keep these variables private inside this closure scope var myGrades = ; var average = function() { var total = myGrades.reduce(function(accumulator, item) { return accumulator + item}, 0); return "Your average grade is " + total / myGrades.length + "."; } var failing = function(){ var failingGrades = myGrades.filter(function(item) { return item < 70;}); return "You failed " + failingGrades.length + " times."; } console.log(failing()); }());

Таким образом, у нашей анонимной функции есть своя область видимости или "замыкание" и мы можем сразу её выполнить. Такой способ позволяет нам скрыть переменные из родительской (глобальной) области видимости.

Что действительно хорошо в таком подходе, дак это то что вы можете использовать локальные переменные внутри этой функции и не бояться случайно перезаписать глобальные переменные с таким же именем. Но доступ к глобальным переменным у вас по прежнему остаётся, например так:

Var global = "Hello, I am a global variable:)"; (function () { // We keep these variables private inside this closure scope var myGrades = ; var average = function() { var total = myGrades.reduce(function(accumulator, item) { return accumulator + item}, 0); return "Your average grade is " + total / myGrades.length + "."; } var failing = function(){ var failingGrades = myGrades.filter(function(item) { return item < 70;}); return "You failed " + failingGrades.length + " times."; } console.log(failing()); console.log(global); }()); // "You failed 2 times." // "Hello, I am a global variable:)"

Пример 2: Глобальный импорт

Другим популярным подходом, который используют библиотеки такие как jQuery, является глобальный импорт. Он похож на замыкания, которые мы только что видели, только теперь мы передаём глобальные переменные в качестве параметров.

(function (globalVariable) { // Keep this variables private inside this closure scope var privateFunction = function() { console.log("Shhhh, this is private!"); } // Expose the below methods via the globalVariable interface while // hiding the implementation of the method within the // function() block globalVariable.each = function(collection, iterator) { if (Array.isArray(collection)) { for (var i = 0; i < collection.length; i++) { iterator(collection[i], i, collection); } } else { for (var key in collection) { iterator(collection, key, collection); } } }; globalVariable.filter = function(collection, test) { var filtered = ; globalVariable.each(collection, function(item) { if (test(item)) { filtered.push(item); } }); return filtered; }; globalVariable.map = function(collection, iterator) { var mapped = ; globalUtils.each(collection, function(value, key, collection) { mapped.push(iterator(value)); }); return mapped; }; globalVariable.reduce = function(collection, iterator, accumulator) { var startingValueMissing = accumulator === undefined; globalVariable.each(collection, function(item) { if(startingValueMissing) { accumulator = item; startingValueMissing = false; } else { accumulator = iterator(accumulator, item); } }); return accumulator; }; }(globalVariable));

В этом примере GlobalVariable единственная глобальная переменная. Преимущество такого подхода в том, что все глобальные переменные вы объявляете заранее, что делает ваш код прозрачным для остальных.

Пример 3: Объектный интерфейс

Ещё один подход при создании модулей заключается в использовании автономных, объектных интерфейсов, например так:

Var myGradesCalculate = (function () { // Keep this variable private inside this closure scope var myGrades = ; // Expose these functions via an interface while hiding // the implementation of the module within the function() block return { average: function() { var total = myGrades.reduce(function(accumulator, item) { return accumulator + item; }, 0); return"Your average grade is " + total / myGrades.length + "."; }, failing: function() { var failingGrades = myGrades.filter(function(item) { return item < 70; }); return "You failed " + failingGrades.length + " times."; } } })(); myGradesCalculate.failing(); // "You failed 2 times." myGradesCalculate.average(); // "Your average grade is 70.33333333333333."

Как вы могли заметить, такой подход позволяет решать какие переменные (методы) мы хотим сделать приватными (например, myGrades ), а какие публичными поместив их в возвращаемый объект (например, average и failing ).

Пример 4: Паттерн "Раскрывающийся модуль"

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

Var myGradesCalculate = (function () { // Keep this variable private inside this closure scope var myGrades = ; var average = function() { var total = myGrades.reduce(function(accumulator, item) { return accumulator + item; }, 0); return"Your average grade is " + total / myGrades.length + "."; }; var failing = function() { var failingGrades = myGrades.filter(function(item) { return item < 70; }); return "You failed " + failingGrades.length + " times."; }; // Explicitly reveal public pointers to the private functions // that we want to reveal publicly return { average: average, failing: failing } })(); myGradesCalculate.failing(); // "You failed 2 times." myGradesCalculate.average(); // "Your average grade is 70.33333333333333."

Это может показаться большим объёмом информации, но это только верхушка айсберга, когда дело доходит до паттернов модулей. Вот некоторые полезные ресурсы, которые нашёл проводя свои исследования:

  • от Addy Osmani: это клад с деталями и выразительно кратким содержанием;
  • Adequately Good by Ben Cherry : полезный обзор с примерам расширенного использования паттерна "Модуль";
  • Blog of Carl Danley : обзор паттерна "Модуль" и ресурсы для других JavaScript паттернов;

CommonJS и AMD

У всех перечисленных выше подходов есть одно общее свойство: они все создают одну глобальную переменную, в которую помещают код функции, создавая тем самым приватное пространство имён и используют область видимости замыкания.

Хоть каждый из этих подходов и эффективен по своему, у них есть и свои недостатки.

Вы как разработчик должны знать правильный порядок загрузки файлов. Например, предположим, что вы используете Backbone в своём проекте, поэтому вы подключаете скрипт Backbone"а через тег в своём файле. Так как Backbone напрямую зависит от Underscore.js, вы не можете подключить скрипт Backbone.js перед Underscore.js.

Управление зависимостями для разработчика иногда доставляет головную боль.

Другой недостаток заключается в том, что всё ещё может произойти конфликт пространств имён. Например, у вас может быть два модуля с одинаковыми именами или две версии одного модуля и они обе вам нужны.

Исходя из этого возникает интересный вопрос: можем ли мы обращаться к интерфейсу модуля не через глобальную область видимости?

К счастью, ответ да.

Есть два популярных и хорошо реализованных подхода: CommonJS и AMD.

CommonJS

CommonJS - это добровольная группа разработчиков, которая проектируют и реализует JavaScript API для объявления модулей.

Модуль CommonJS - это фрагмент JavaScript кода предназначенный для многократного использования. Он экспортирует специальные объекты, делая их доступными для других модулей, чтобы они могли включать их в свои зависимости. Если вы программировали на Node.js, то вам это будет очень хорошо знакомо.

С CommonJS в каждом JavaScript файле модуль хранится в своём собственном уникальном контексте (так же, как и в замыканиях). В этом пространстве мы используем объект module.exports чтобы экспортировать модули, и require чтобы подключить их.

Определение CommonJS модуля может выглядеть следующим образом:

Function myModule() { this.hello = function() { return "hello!"; } this.goodbye = function() { return "goodbye!"; } } module.exports = myModule;

Мы используем специальный объект module и размещаем ссылку на нашу функцию в module.exports . За счёт этого CommonJS знает, что мы хотим открыть модуль так, чтобы другие файлы могли его использовать.

После этого, когда кто-то захочет использовать наш myModule , он без проблем сможет его подключить следующим образом:

Var myModule = require("myModule"); var myModuleInstance = new myModule(); myModuleInstance.hello(); // "hello!" myModuleInstance.goodbye(); // "goodbye!"

У данного подхода есть два очевидных преимущества над подходами, которые мы обсуждали раньше:

  1. Отсутствие загрязнения глобального пространства имён;
  2. Становление наших зависимостей более явными;

Кроме того, очень компактный синтаксис, я это очень люблю.

Нужно отметить, что CommonJS использует server-first подход и модули загружаются синхронно. Это важно потому что если у нас есть ещё три модуля, которые нам нужно подключить, он будет загружать их один за другим.

Сейчас это прекрасно работает на сервере, но к сожалению, это затрудняет написание браузерного JavaScript. На получение модуля из интернета уходит намного больше времени, чем на получение модуля с жёсткого диска. Пока скрипт загружает модуль, браузер блокируется и вы ничего не можете сделать, до тех пор, пока он не закончит загрузку. Он ведёт себя так потому что JavaScript поток останавливается, пока загружается код (Я расскажу вам как мы может обойти эту проблему во второй части статьи, когда мы будем рассматривать сборку модулей. На данный момент это всё, что нам нужно знать).

AMD

CommonJS хорош, но что если нам нужно загружать модули асинхронно? Ответ на этот вопрос "Асинхронное определение модулей (Asynchronous Module Definition)" или просто AMD.

Define(["myModule", "myOtherModule"], function(myModule, myOtherModule) { console.log(myModule.hello()); });

Функция определения модуля принимает первым аргументом массив зависимостей. Эти зависимости загружаются в фоновом (не блокирующим) режиме и вызывают функцию обратного вызова, которая была передана вторым аргументом.

Далее, функция обратного вызова в качестве аргументов принимает уже загруженные зависимости, и позволяет использовать их. И наконец сами зависимости тоже должны быть определены с помощью ключевого слова define .

Например, myModule может выглядеть следующим образом:

Define(, function() { return { hello: function() { console.log("hello"); }, goodbye: function() { console.log("goodbye"); } }; });

Итак, давайте пройдёмся ещё раз. В отличии от CommonJS, AMD реализует browser-first подход вместе с асинхронным поведением (Обратите внимание, что достаточно много людей утверждают, что динамическая загрузка файлов не благоприятна при выполнении вашего кода. Об этом мы поговорим больше в следующей части нашей статьи, посвящённой сборке модулей).

Кроме асинхронности, у AMD есть ещё одно преимущество. AMD модули могут быть функциями, конструкторами, строками, JSON"ом и другими типами, в то время как CommonJS в качестве модулей поддерживает только объекты.

Из минусов, AMD не совместим с io, файловой системой и другими серверно-ориентированными особенностями, которые доступны через CommonJS. И синтаксис функции объявления модулей многословен в сравнении с просты require .

UMD

Для проектов, которые требуют поддержки функций обеих систем AMD и CommonJS, есть ещё один формат. Универсальное Объявление Модулей (Universal Module Definition), ну или по простому UMD.

По существу, UMD предоставляет возможность использование любого из двух вышеперечисленных способов, плюс поддерживает определение модулей через глобальную переменную. В результате, модули UMD могут работать как на клиенте, так и на сервере.

Быстрый пример того, как работает UMD:

(function (root, factory) { if (typeof define === "function" && define.amd) { // AMD define(["myModule", "myOtherModule"], factory); } else if (typeof exports === "object") { // CommonJS module.exports = factory(require("myModule"), require("myOtherModule")); } else { // Browser globals (Note: root is window) root.returnExports = factory(root.myModule, root.myOtherModule); } }(this, function (myModule, myOtherModule) { // Methods function notHelloOrGoodbye(){}; // A private method function hello(){}; // A public method because it"s returned (see below) function goodbye(){}; // A public method because it"s returned (see below) // Exposed public methods return { hello: hello, goodbye: goodbye } }));

Нативный JS

Фууух. Вы ещё рядом? Я не потерял вас в этом лесу? Хорошо! Потому что у нас есть ещё один тип определения модулей.

Как вы могли заметить выше, ни один из модулей не был родным для JavaScript. Вместо этого мы имитировали систему модулей, используя либо паттерн "Модуль", либо CommonJS, либо AMD.

К счастью умные люди из TC39 (the standards body that defines the syntax and semantics of ECMAScript) добавили встроенные модули в ECMAScript 6 (ES6).

ES6 предлагает разнообразные возможности для импорта и экспорта модулей, про которые рассказывали не один раз. Вот парочка ресурсов:

  • jsmodules.io
  • exploringjs.com

Что хорошего в ES6 модулях в сравнении с CommonJS и AMD? То, что он собрал лучшее из двух миров: компактность, декларативный синтаксис, асинхронную загрузку, плюс дополнительные преимущества, такие как циклические зависимости.

Вероятно, моя любимая особенность ES6 модулей заключается в том, что импорты это живые read-only виды экспортов (в сравнении с CommonJS, где импорт это просто копия экспорта).

Вот пример того, как это работает:

// lib/counter.js var counter = 1; function increment() { counter++; } function decrement() { counter--; } module.exports = { counter: counter, increment: increment, decrement: decrement }; // src/main.js var counter = require("../../lib/counter"); counter.increment(); console.log(counter.counter); // 1

В этом примере мы сделали две копии базового модуля: первый раз когда экспортировали его, а второй раз когда импортировали.

Кроме того, копия которая находится в main.js теперь отключена от оригинального модуля. Поэтому, даже когда мы увеличиваем наш счётчик, он всё равно возвращает 1, так как counter - это переменная которую мы импортировали из модуля, отключённая от оригинального модуля.

Таким образом, увеличение счётчика будет увеличивать его в модуле, но не будет увеличивать в скопированной версии. Единственный способ изменить скопированную версию, это увеличить её вручную:

Counter.counter++; console.log(counter.counter); // 2

А, ES6 при импорте создает живой read-only вид модуля:

// lib/counter.js export let counter = 1; export function increment() { counter++; } export function decrement() { counter--; } // src/main.js import * as counter from "../../counter"; console.log(counter.counter); // 1 counter.increment(); console.log(counter.counter); // 2

Интересно, не правда ли? Что я нахожу действительно убедительным в живых read-only видах, дак это то, что они позволяют разделить ваши модули на более мелкие куски без потери функциональности.

Вы можете развернуть и объединить их заново (в новом проекте), и никаких проблем. Всё будет работать.

Немного заглядывая вперёд: Сборка модулей

Вау! Как быстро пролетело время. Это было крутое путешествие и я искренне надеюсь, что статья дала вам возможность лучше понять модули в JavaScript.

В следующей статье мы рассмотрим связывание модулей, затронем основные темы, включая:

  • Почему мы собираем модули;
  • Различные подходы к сборке;
  • API загрузчика модулей ECMAScript;
  • И ещё много всего...

Напоминаю, что это вводный курс по JavaScript для начинающих . Сегодня мы рассмотрим, какие операторы существуют в JavaScript . Пристегните ремни! Букаф будет много.

Когда у вас есть кучка данных, то с ними нужно что-то сделать. Делами-то как раз и заняты операторы. Они вечно что-то складывают, перемножают, делят, отнимают, сравнивают, присваивают и еще черти чем занимаются. Без операторов в программировании просто каши не сваришь.

В языке JavaScript используются следующие виды операторов:

  • Арифметические операторы
  • Операторы присваивания
  • Операторы сравнения
  • Логические операторы
  • Строковые операторы
  • Условные операторы

Это далеко не полный список, но для начала хватит и этого за глаза. Разберем каждый вид представленных операторов, зачем они нужны и с чем их едят. Поехали!

Арифметические операторы в JavaScript

С арифметическими операторами все вы знакомы еще со школы. Это обычные знаки сложения, вычитания, деления и умножения: + , - , / , * . Соответственно, выполняют они в программировании те же самые функции, что и в обычной математике. Сложностей с этим у вас не возникнет.

Данные, с которыми работают операторы, называются операндами .

2 + 3 // здесь числа 2 и 3 - операнды, а знак + - оператор сложения

Как и в математике у арифметических операторов есть свои приоритеты: умножение и деление имеют более высокий приоритет, чем сложение и вычитание.

2 + 3 * 4 // здесь сначала выполняется умножение, а уже потом сложение

И так же, как в математике, активно используются скобки, чтобы изменить приоритет:

(2 + 3) * 4 // здесь сначала выполняется сложение, а потом умножение

Кстати, знак = тоже оператор. Как мы уже выяснили в статье про , это оператор присваивания, а вовсе не знак равенства. Не забывайте об этом!

Оператор деления по модулю

А теперь рассмотрим более интересные арифметические операторы. И первым станет значок процентов - % . В JavaScript это не проценты от слова совсем. Так в программировании обозначается деление по модулю. Результатом такой операции будет остаток от деления. Например:

100 % 22 // остаток будет равен 12
100 % 10 // остаток будет равен 0

При вычислениях этот оператор имеет тот же приоритет, что и умножение с делением, так что не забывайте ставить скобочки.

Совмещение операторов

Оператор = можно и нужно совмещать с другими операторами, чтобы сократить запись. Пример:

var n = 2; // присваиваем переменной n значение 2
n = n + 3; // присваиваем переменной n новое значение n + 2, получаем 5

То же самое можно записать так:

var n = 2;
n += 3; // равносильно записи n = n + 3

Операторы инкремента ++ и декремента – –

Среди арифметических операторов есть парочка весьма любопытных - инкремент и декремент . Обозначаются соответственно ++ и –– . Первый увеличивает переменную на единицу, а второй уменьшает. Эта фишка очень часто используется в программировании, так как предоставляет массу удобств. Чаще всего ее можно встретить в условных выражениях, но об этом позже.

У обоих операторов есть определенное местоположение в записи. Они могут быть как в префиксной форме (перед переменной) ++n , так и в постфиксной (после переменной) n++ . Разница огромна! Никогда не путайте эти формы и хорошенько запомните их. Если эти операторы стоят перед переменной, то в результате увеличивают ее значение на 1. Но! Если они стоят после переменной, то возвращают исходное значение. Пример:

var n = 2, m = 0;
m = ++n // увеличивает n на 1 (n = 3) и присваивает m тоже значение 3

var n = 2, m = 3;
m = n++ // увеличивает n на 1 (n = 3), но присваивает m предыдущее значение n = 2

С первым примером, уверен, вы легко разберетесь. А вот со вторым могут возникнуть проблемы. Чтобы легче было понять эту штуку и не запутаться, просто представьте, что вы сначала присвоили переменной m значение переменной n , а уже после этого увеличили значение n на единицу. В первом же примере вы сначала увеличили значение n на единицу, а потом уже присвоили это значение переменной m .

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

Операторы сравнения

И снова вспоминаем математику. Знаки знакомы всем и каждому. В программировании они называются операторами сравнения . В JavaScript используются следующие операторы сравнения:

< меньше
> больше
<= меньше или равно
>= больше или равно
== равно
!= не равно
=== строго равно
!== строго не равно

Обратите внимание, что знак «больше или равно» пишется именно так >= , а не => . То есть, стрелка стоит перед знаком равно, а не после него.

Операторы сравнения позволяют сравнивать значения переменных и результатом этой операции всегда выступает булево значение true или false . Обычно их применяют в условных выражениях. От того, что дает результат сравнение зависит, какая часть кода будет выполняться дальше.

В JavaScript можно сравнивать разные типы данных одновременно, например, число и строку:

12345 == "12345" // true

Просто в этом случае строка автоматически преобразуется в число. Строгое же равенство === или неравенство!== используются только при сравнении переменных одинакового типа.

Логические операторы

Логические операции в JavaScript - это одна из довольно мудреных тем для начинающих. С ней стоит хорошенько разобраться, чтобы успешно идти дальше в освоении языка. Они чаще всего применяются вместе с операциями сравнения и выдают булево значение true или false .

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

&& (И)
|| (ИЛИ)
! (НЕ)

Логический оператор && (И)

Оператор && выполняет операцию «логическое И» над двумя значениями. При этом он возвращает true тогда и только тогда, когда оба операнда имеют значение true . Если же один или оба операнда имеют значение false , то оператор возвращает false . Примеры:

2 < 3 && 4 < 5 // true
2 < 3 && 5 < 4 // false
3 < 2 && 5 < 4 // false

У логических операторов приоритет меньше, чем у операторов сравнения, поэтому в приведенных примерах мы обходимся без скобок. Понятно, что сначала мы сравниваем числа между собой, а уже потом применяем логику.

Логический оператор || (ИЛИ)

С логическим оператором || (ИЛИ) другая песня. Оператор || выполняет операцию «логическое ИЛИ» над двумя операндами. Если один или оба операнда имеют истинное значение, он возвращает истинное значение. Если оба операнда имеют ложные значения, он возвращает ложное значение. Примеры:

2 < 3 || 4 < 5 // true
2 < 3 || 5 < 4 // true
3 < 2 || 5 < 4 // false

У логических операторов есть одна хитрость. Они не любят лишней работы. Как, впрочем, и я. Свои вычисления они всегда начинают слева направо. И если первая часть выражения соответствует их условиям, то остальную часть выражения они даже не вычисляют.

К примеру, если оператор || находит истинное значение в самом начале, то сразу дает истинное значение, а остальное уже не проверяет. Также и оператор && , если находит в самом начале ложное выражение, то сразу дает ложный результат, а остальную часть не проверяет.

И еще одна фишка: приоритет у оператора И && больше, чем у ИЛИ || , поэтому он выполняется раньше:

2 < 3 || 4 < 5 && 5 < 6 // здесь сначала будет выполнено вычисление в правой части, а потом уже в левой

Логический оператор! (НЕ)

Логический оператор! обозначает «логическое НЕ». Он используется только с одним операндом и меняет значение этого операнда на противоположное. Если значение n истинно, то значение!n будет ложным. Так как этот оператор можно прикрутить лишь к одному операнду, то чтобы инвертировать целое выражение, его надо взять в скобки!(n && m) .

Строковые операторы

Про строковые операторы мы уже говорили ранее. Это тот самый плюс + что используется для соединения строковых переменных, или иначе - для конкатенации (сложения строк). Пример:

"Игорь " + "Квентор" == "Игорь Квентор"

Обратите внимание, что в первом слове добавлен пробел перед закрывающей кавычкой. Если его не добавить, то строки сольются в одно слово "ИгорьКвентор" .

У этого оператора есть одна особенность: если в выражении есть хоть одна строка, то он и все остальные аргументы приводит к строковому типу. Например:

Остальные арифметические операторы работают только с числами и всегда приводят аргументы к числу.

Условные операторы

В JavaScript есть два условных оператора if и?: Хотя, если быть точным, то if - это вообще-то управляющая инструкция и довольно обширная, с кучей плюшек и интересных особенностей. Поэтому о ней будет отдельная статья. Пока же рассмотрим более простой условный оператор?:

Обычно этот оператор записывается как?: Но в программах он выглядит иначе. Он имеет три операнда. Первый операнд предшествует символу? , второй стоит между символами? и: третий - после:

условие? значение 1: значение 2

Смысл его работы прост: если выполняется поставленное условие, то возвращается значение 1, если же нет - значение 2. Этот условный оператор часто служит более простой заменой инструкции if , когда в последней нет особой необходимости. При этом и сама запись сокращается и ее легче прочесть.

Пока на этом все!

Надеюсь, с операторам в JavaScript вы теперь немного разобрались. А чтобы мозг не закипел, вот вам короткий мульт для расслабления - Программист может все! :)


Арифметические операторы и приведение типов

JavaScript поддерживает следующие арифметические операторы:

Интересная особенность JavaScript - возможность выполнять арифметические операции над переменными различного типа. В этом случае интерпретатор самостоятельно выполняет приведение типов и выполняет указанную операцию. В процессе ведения типов используются следующие правила:

1. Если один из операндов - строка, то все остальные операнды приводятся к строковому виду.

Var1 = "Дядя" var2 = "Ваня" result = var1 + " " + var2 // result = "Дядя Ваня" mixed = var2 + 100 // mixed = "Ваня100"

2. Все логические операнды приводятся к числовому виду, кроме случаев, когда все операнды в выражении логические. При этом true приводится к "1", а false - к "0". При сочетании логических операндов со строками - все операдны переводятся в текстовый вид.

Var1 = true var2 = true result = var1 + var2 // result = 2 mixed = var2 + 100 // mixed = 101 var3 = "строка:" str = var3 + var1 // str = "строка:true"

3. Если приведение типов выполнить не удалось - результатом выражения будет "NaN" (например, при попытке разделить строку на что-либо).

Var1 = "Дядя" var2 = "Ваня" result = var1 / var2 // result = "NaN" mixed = var2 * true // mixed = "NaN"

Однако на начальном этапе лучше воздержаться от приведения типов и фокусов с преобраованием результатов. Это избавит вас от значительного числа ошибок.

Объект Math

Объект Math содержит основные математические константы и стандартные математические функции. Наиболее часто используемые приведены в таблице:

Свойства
LN10 Значение натурального логарифма числа 10
LN2 Значение натурального логарифма числа 2
PI Значение числа Пи
Методы
abs(число) Возвращает абсолютное значение числа (т.е. число без учёта его знака)
ceil(число) Откругляет число до ближайшего большего целого (округление "вверх")
exp(число) Возвращает число "e" в степени "число"
floor(число) Откругляет число до ближайшего меньшего целого (округление "вниз")
max(число1, число2) Возвращает большее из двух чисел
min(число1, число2) Возвращает меньшее из двух чисел
pow(число1, число2) Возвращает "число1", возведённое в степень "число2"
random() Возвращает случайное число в диапазоне от 0 до 1
round(число) Округляет число в соответствии со стандартными правилами округления
sqrt(число) Возвращает квадратный корень числа.

Из всех перечисленных функций имеет смысл дополнительно пояснить только ceil(), floor() и round(). Рассмотрим их отличия на примере:

Num = 1.222 // ближайшее целое "вниз" - 1 // ближайшее целое "вверх" - 2 // арифметически откругляется до 1 alert(Math.ceil(num)) alert(Math.floor(num)) alert(Math.round(num)) // получим три сообщения: 2, 1, 1 num = 1.777 // ближайшее целое "вниз" - 1 // ближайшее целое "вверх" - 2 // арифметически откругляется до 2 alert(Math.ceil(num)) alert(Math.floor(num)) alert(Math.round(num)) // получим три сообщения: 2, 1, 2

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