1. Переменные
Переменные используются для хранения данных и состоят из идентификатора (имени) и области в памяти, где хранится их значение. Переменную можно представить как коробку с названием, в которой что-то лежит (значение).
1.1. Имена переменных
Идентификатор — это имя переменной, функции, параметра или класса. Состоит из одного или нескольких символов в следующем формате:
- Первым символом должна быть буква a-z или A-Z , символ подчеркивания _ или знак доллара $ .
- Другие символы могут быть буквами a-z , A-Z , цифрами 0-9 , подчеркиваниями _ и знаками доллара $ .
- Все идентификаторы чувствительны к регистру. Это значит, что переменные user , usEr и User различны.
Имя переменной должно быть понятным.
❌ Плохо chislo korzina_tovarov profil_polzovatelya tekushiy_mesyaz ✅ Хорошо number cart userProfile currentMonth
Использование camelCase-нотации для идентификаторов это стандарт. При таком подходе, первое слово пишется строчными буквами, а каждое дополнительное начинается с прописной буквы. К примеру: user , greetUser , getUserData , isActive , activeGuestCount , totalWorkerSalary .
- Как называть переменные
- JavaScript naming conventions
1.2. Ключевые слова
Есть список зарезервированных ключевых слов, которые имеют специальное значение и используются для определенных конструкций. Нельзя использовать ключевые слова как идентификаторы.
1.3. Объявление переменных
В современном JavaScript есть 2 ключевых слова для объявления переменных.
- let — объявляет переменную, которой можно присвоить новое значение после объявления, то есть переопределить.
- const — так же объявляет переменную, но с одним отличием: объявляемая переменная должна быть немедленно инициализирована значением, и этой переменной не может быть присвоено другое значение после инициализации.
Создание переменной без ключевого слова let или const приведет к ошибке.
/* * Для объявления переменной используются ключевые слова let и const, * за которыми следует имя переменной. * Переменным объявленным через let не обязательно сразу присваивать значение. */ let age; /* * Переменные объявленые как const обязательно должны быть инициализированы * значением во время объявления, иначе будет ошибка. */ const yearOfBirth = 2006; /* * Значение переменной можно получить обратившись к ней по имени. * console.log() используется для вывода данных в консоль разработчика, * с этим методом более детально познакомимся позже. */ console.log(yearOfBirth); // 15 /* * Eсли перемнной объявленной как let, изначально не присвоено значение, * она инициализируется специальным значением undefined (не определено). */ console.log(age); // undefined /* * Если переменная объявлена как let, ее значение можно перезаписать. */ age = 14; console.log(age); // 14 /* * Если переменная объявлена как const, перезаписать ее значение нельзя. * При попытке присвоить новое значение будешь ошибка выполнения скрипта. */ yearOfBirth = 2020; // будет ошибка
1.4. Когда использовать const и let
Единственное отличие const и let состоит в том, что const запрещает повторное присваивание переменной какого-либо значения. Объявление const делает код более читаемым, так как переменная всегда ссылается на одно и то же значение. В случае с let такой уверенности нет.
Будет разумно использовать let и const так:
- Используйте const по умолчанию, большинство переменных будут объявлены именно так.
- Используйте let , если потребуется присвоить переменной другое значение во время выполнения скрипта.
1.5. Обращение к переменной
Важно различать неопределенные и необъявленные переменные.
- Неопределенная (undefined) — это переменная, которая была объявлена, но не инициализирована значением, по умолчанию ей присваевается значение undefined как начальное.
- Необъявленная (undeclared или not defined) — это переменная, которая не была объявлена в доступной области видимости.
let name; /* * Переменная name объявлена, но не инициализирована, поэтому * ее значение undefined (не определено), тогда как переменная age * не была объявлена, поэтому доступ к ней вызывает ошибку ReferenceError */ console.log(name); // undefined console.log(age); // ReferenceError: age is not defined
Попытка обратиться к пременной до ее объявления вызовет ошибку.
// console.log(a); // ReferenceError: a is not defined let a = 10; console.log(a); // 10 // console.log(b); // ReferenceError: b is not defined const b = 15; console.log(b); //15
2. Типы примитивов
JavaScript это динамический, слабо типизированный язык, поэтому переменная не ассоциируется с каким-либо типом данных, тип есть у значения переменной. То есть переменная может хранить значения разных типов.
2.1. Number
Целые числа и числа с плавающей запятой. После объявления переменной, можно инициализировать ее числовым значением.
const age = 20; const number = 5.8;
2.2. String
Строки это просто текст, последовательность из нуля или более символов. Строка начинается и заканчивается одиночной ‘ , или двойными кавычками » . Открывающая и закрывающая кавычки должны быть одинаковые.
const name = 'Mango';
2.3. Boolean
Логический тип данных, флаги состояния. Имеет только два значения: true и false , в нижнем регистре. Например на вопрос включен ли свет в комнате можно ответить да ( true ) или нет ( false ).
- true — да, верно, истина, 1
- false — нет, неверно, ложь, 0
Обратите внимание на имена переменных содержащих буль. Они задают вопрос, и ответ на его — да или нет.
const isAuthenticated = true; const canMerge = false; const hasChildren = true; const isModalOpen = false;
2.4. null
Особое значение, которое по сути значит ничто . Используется в тех ситуациях, когда необходимо явно указать отсутсвие значения. К примеру если в базе данных не нашли пользователя, то можно сказать что значение null .
// Переменная guest пуста и лишена значения const guest = null;
2.5. undefined
Еще одно специальное значение. По умолчанию, когда переменная объявляется, но не инициализируется, ее значение не определено, ей присваивается undefined .
let name; console.log(name); // undefined
2.6. Оператор typeof
Для получения типа значения переменной, используется оператор typeof , который возвращает на место своего вызова тип значения переменной указанного после него. Возвращаемое значение это просто строка в которой указан тип.
// undefined let a; console.log(typeof a); // "undefined" // null const b = null; console.log(typeof null); // "object" // Number const c = 5; console.log(typeof c); // "number" // String const d = 'JavaScript is awesome!'; console.log(typeof d); // "string" // Boolean const e = false; console.log(typeof e); // "boolean"
results matching » «
No results matching » «
Синтаксис JavaScript. Рекомендации.
JavaScript чувствителен к регистру символов. Это означает, что имена Name и name представляют собой совершенно разные имена переменных или функций. Чувствительность к регистру касается как стандартных, так и пользовательских элементов сценария.
Имена переменных чувствительны к регистру; имена переменных должны состоять из букв латинского алфавита, цифр и знаков «_»; имена переменных не могут начинаться с цифры; переменная должна быть объявлена до того, как вы начнете ее использовать;
Принято если переменная состоит из двух слов, то первое слово пишется с маленькой буквы, например: timerId.
Имена CSS в JavaScript. В именах многих атрибутов Основы работы с CSS стилей, таких как font-family, содержатся дефисы. В JavaScript дефис интерпретируется как знак минус, поэтому нельзя на писать, например, такое выражение: element.style.font-family = «sans serif»; Таким образом, если имя CSS атрибута содержит дефисы, имя свойства объекта JavaScript образуется путем удаления дефисов и перевода в верхний регистр буквы, непосредственно следующей за каждым из них. Другими словами, атрибут border-left-width доступен через свойство borderLeftWidth, а к атрибуту font-family можно обратиться следующим образом:
element.style.fontFamily = "sans-serif";
Операторы должны разделяться между собой символом «;» хотя это и не является обязательным.
Операторы могут объединяться в блоки. Для этого их следует размещать в фигурных скобках: «<" и ">«. В этом случае несколько операторов рассматриваются интерпретатором и выполняются как один составной оператор. При этом разделитель «;» после блока не ставится.">
В текст скрипта можно включать однострочные и блочный комментарии, аналогично С++.
В JavaScript значению «ложь» соответствует не только логический литерал false, но и несколько эквивалентов других типов данных: 0, «», null, undefined. Все остальные значения считаются эквивалентом «истины» — true. Таким образом, в логических выражениях могут участвовать значения не булевого типа.
Если браузер не понимает JS и вы не хотите чтобы скрипт выводился как обычный текст, применяют такой приём с комментариями:
Пробелы и переводы строк в JavaScript не обязательны. Можно написать программу в одну строку. Роль пробелов в блоках – отделять их от остальной программы.
Переменные в JavaScript
Переменные в JavaScript объявляются при помощи ключевого слова var, которое располагается в начале объявления. При объявлении переменной можно присвоить значение (проинициализировать). Если переменная не была проинициализирована при объявлении, она будет содержать специальное значение — undefined (неопределено). В некоторых случаях переменную инициализировать все же необходимо, но ей не нужно присваивать конкретное значение. В такой ситуации можно воспользоваться еще одним специальным значением null (пусто). За один раз можно объявить более одной переменной, в таком случае они разделяются запятой «,». Объявление заканчивается символом «;».Шаблон объявления переменных имеет следующий вид (в квадратные скобки [] взяты необязательные части конструкции):
var имя_переменной [= значение] [, имя_переменной [= значение] . ];
Ниже приводятся несколько примеров объявления переменных:
var angle; //переменная объявлена но не проинициализирована var counter_1 = 0; //переменная объявлена и проинициализирована значением 0 var FirstName, LastName = "Иванов", age=20; //объявление нескольких переменных одновременно
Операторы цикла for, while, do
Синтаксис цикла for:
for (A; B; I) где A - первоначальное выражение, B - условие продолжения. Если это выражение ложно, то выполнение цикла завершается, I - выражение инкремента, <> - тело цикла, S - операторы.
Пример. В цикле перебирается строка с 0 -ого элемента и до последнего символа.
var str='Моя строка'; for (i = 0; i str.length; i++) { console.log(str[i]); }
Синтаксис цикл while:
while (B) где B - условие продолжения. Если это выражение ложно, то выполнение цикла завершается, <> - тело цикла, S - операторы.
Иными словами, операторы S, выполняются до тех пор, пока условие B не станет ложным. Например
var number = 0; while (number 12) { console.log(number); number = number + 2; }
Синтаксис цикл do. Он похож на цикл while. Отличается только в одном: цикл do всегда выполняет тело хотя бы один раз, а проверяет условие после первого выполнения. Поэтому и тестируемое выражение записывают после тела цикла:
do { var name = prompt("Who are you?"); } while (!name); console.log(name);
Эта программа заставляет ввести имя. Она спрашивает его снова и снова, пока не получит что-то кроме пустой строки. Добавление «!» превращает значение в булевское и затем применяет логическое отрицание, а все строки, кроме пустой, преобразуются в булевское true.
Тернарный оператор
Для избежания ошибок сравнение лучше заключать в скобки. Например, на странице есть элемент чекбокс и нужно проверить его состояние
var checknodns = document.getElementById('checknodns'); var test = (checknodns.checked) ? "true" : "false";
switch case
Когда выполняется инструкция switch, она вычисляет значение выражения, а затем ищет метку case, соответствующую этому значению. Если метка найдена, исполняется блок кода, начиная с первой инструкции, следующей за меткой case. Если метка case с соответствующим значением не найдена, исполнение начинается с первой инструкции, следующей за специальной меткой default:. Если метки default: нет, блок кода пропускается целиком.
Следующая инструкция switch эквивалентна повторяющимся инструкциям if/else:
switch(n) { case 1: // Выполняется, если n == 1 // Исполняем блок кода 1. break; // Здесь останавливаемся case 2: // Выполняется, если n == 2 // Исполняем блок кода 2. break; // Здесь останавливаемся case 3: // Выполняется, если n == 3 // Исполняем блок кода 3. break; // Здесь останавливаемся default: // Если все остальное не подходит. // Исполняем блок кода 4. break; // Здесь останавливаемся }
Инструкция break приводит к передаче управления в конец инструкции switch или цикла. Конструкции case в инструкции switch задают только начальную точку исполняемого кода, но не задают никаких конечных точек. В случае отсутствия инструкций break инструкция switch начинает исполнение блока кода с метки case, соответствующей значению выражения, и продолжает исполнение до тех пор, пока не дойдет до конца блока. В редких случаях это полезно для написания кода, который переходит от одной метки case к следующей, но в 99 % случаев следует аккуратно завершать каждый блок case инструкцией break.
При использовании switch внутри функции можно помещать вместо break инструкцию return. Обе эти инструкции служат для завершения работы инструкции switch и предотвращения перехода к следующей метке case.
Ниже приводится более реальный пример использования инструкции switch; он преобразует значение в строку способом, зависящим от типа значения:
function convert(x) < switch(typeof x) < case 'number': // Преобразуем число в шестнадцатеричное целое return x.toString(16); case 'string': // Возвращаем строку, заключенную в кавычки return '"' + x + '"'; case 'boolean': // Преобразуем в TRUE или FALSE, в верхнем регистре return x.toString().toUpperCase(); default: // Любой другой тип преобразуем обычным способом return x.toString() >>
Стандарт ECMAScript v3 допускает указание после case произвольного выражения, например case a[0]:, case n+1:.
Руководство по JavaScript, часть 3: переменные, типы данных, выражения, объекты
Сегодня, в третьей части перевода руководства по JavaScript, мы поговорим о разных способах объявления переменных, о типах данных, о выражениях и об особенностях работы с объектами.
Переменные
Переменная представляет собой идентификатор, которому присвоено некое значение. К переменной можно обращаться в программе, работая таким образом с присвоенным ей значением.
Сама по себе переменная в JavaScript не содержит информацию о типе значений, которые будут в ней храниться. Это означает, что записав в переменную, например, строку, позже в неё можно записать число. Такая операция ошибки в программе не вызовет. Именно поэтому JavaScript иногда называют «нетипизированным» языком.
Прежде чем использовать переменную, её нужно объявить с использованием ключевого слова var или let . Если речь идёт о константе, применяется ключевое слово const . Объявить переменную и присвоить ей некое значение можно и не используя эти ключевые слова, но делать так не рекомендуется.
▍Ключевое слово var
До появления стандарта ES2015 использование ключевого слова var было единственным способом объявления переменных.
var a = 0
Если в этой конструкции опустить var , то значение будет назначено необъявленной переменной. Результат этой операции зависит от того, в каком режиме выполняется программа.
Так, если включён так называемый строгий режим (strict mode), подобное вызовет ошибку. Если строгий режим не включён, произойдёт неявное объявление переменной и она будет назначена глобальному объекту. В частности, это означает, что переменная, неявно объявленная таким образом в некоей функции, окажется доступной и после того, как функция завершит работу. Обычно же ожидается, что переменные, объявляемые в функциях, не «выходят» за их пределы. Выглядит это так:
function notVar() < bNotVar = 1 //лучше так не делать >notVar() console.log(bNotVar)
В консоль попадёт 1 , такого поведения от программы обычно никто не ждёт, выражение bNotVar = 1 выглядит не как попытка объявления и инициализации переменной, а как попытка обратиться к переменной, находящейся во внешней по отношению к функции области видимости (это — вполне нормально). Как результат, неявное объявление переменных сбивает с толку того, кто читает код и может приводить к неожиданному поведению программ. Позже мы поговорим и о функциях, и об областях видимости, пока же постарайтесь всегда, когда смысл некоего выражения заключается в объявлении переменной, пользоваться специализированными ключевыми словами. Если в этом примере тело функции переписать в виде var bNotVar = 1 , то попытка запустить вышеприведённый фрагмент кода приведёт к появлению сообщения об ошибке (его можно увидеть в консоли браузера).
Выглядеть оно, например, может так: Uncaught ReferenceError: bNotVar is not defined . Смысл его сводится к тому, что программа не может работать с несуществующей переменной. Гораздо лучше, при первом запуске программы, увидеть такое сообщение об ошибке, чем писать непонятный код, который способен неожиданно себя вести.
Если, при объявлении переменной, её не инициализируют, не присваивают ей какого-либо значения, ей автоматически будет присвоено значение undefined .
var a //typeof a === 'undefined'
Переменные, объявленные с помощью ключевого слова var , можно многократно объявлять снова, назначая им новые значения (но это может запутать того, кто читает код).
var a = 1 var a = 2
В одном выражении можно объявить несколько переменных:
var a = 1, b = 2
Областью видимости переменной (scope) называют участок программы, в котором доступна (видима) эта переменная.
Переменная, инициализированная с помощью ключевого слова var за пределами какой-либо функции, назначается глобальному объекту. Она имеет глобальную область видимости и доступна из любого места программы. Если переменная объявлена с использованием ключевого слова var внутри функции, то она видна только внутри этой функции, являясь для неё локальной переменной.
Если в функции, с использованием var , объявлена переменная, имя которой совпадает с именем некоей переменной из глобальной области видимости, она «перекроет» глобальную переменную. То есть, при обращении к такой переменной внутри функции будет использоваться именно её локальный вариант.
Важно понимать, что блоки (области кода, заключённые в фигурные скобки) не создают новых областей видимости. Новая область видимости создаётся при вызове функции. Ключевое слово var имеет так называемую функциональную область видимости, а не блочную.
Если в коде функции объявлена некая переменная, она видна всему коду функции. Даже если переменная объявлена с помощью var в конце кода функции, обратиться к ней можно и в начале кода, так как в JavaScript работает механизм поднятия переменных (hoisting). Этот механизм «поднимает» объявления переменных, но не операции их инициализации. Это может стать источником путаницы, поэтому возьмите себе за правило объявлять переменные в начале функции.
▍Ключевое слово let
Ключевое слово let появилось в ES2015, его, упрощённо, можно назвать «блочной» версией var . Область видимости переменных, объявленных с помощью ключевого слова let , ограничивается блоком, оператором или выражением, в котором оно объявлено, а также вложенными блоками.
Если само слово «let» кажется не очень понятным, можно представить, что вместо него используется слово «пусть». Тогда выражение let color = ‘red’ можно перевести на английский так: «let the color be red», а на русский — так: «пусть цвет будет красным».
При использовании ключевого слова let можно избавиться от неоднозначностей, сопутствующих ключевому слову var (например, не удастся два раза, используя let , объявить одну и ту же переменную). Использование let за пределами функции, скажем, при инициализации циклов, не приводит к созданию глобальных переменных.
Например, такой код вызовет ошибку:
for (let i = 0; i < 5; i++) < console.log(i) >console.log(i)
Если же, при инициализации цикла, счётчик i будет объявлен с использованием ключевого слова var , то i будет доступно и за пределами цикла, после того, как он завершит работу.
В наши дни, при разработке JS-программ на основе современных стандартов, вполне можно полностью отказаться от var и использовать только ключевые слова let и const .
▍Ключевое слово const
Значения переменных, объявленных с использованием ключевых слов var или let , могут быть перезаписаны. Если же вместо этих ключевых слов используется const , то объявленной и инициализированной с его помощью константе новое значение присвоить нельзя.
const a = 'test'
В данном примере константе a нельзя присвоить новое значение. Но надо отметить, что если a — это не примитивное значение, наподобие числа, а объект, использование ключевого слова const не защищает этот объект от изменений.
Когда говорят, что в переменную записан объект, на самом деле имеют в виду то, что в переменной хранится ссылка на объект. Эту вот ссылку изменить не удастся, а сам объект, к которому ведёт ссылка, можно будет изменить.
Ключевое слово const не делает объекты иммутабельными. Оно просто защищает от изменений ссылки на них, записанные в соответствующие константы. Вот как это выглядит:
const obj = <> console.log(obj.a) obj.a = 1 //работает console.log(obj.a) //obj = 5 //вызывает ошибку
В константу obj , при инициализации, записывается новый пустой объект. Попытка обращения к его свойству a , несуществующему, ошибки не вызывает. В консоль попадает undefined . После этого мы добавляем в объект новое свойство и снова пытаемся обратиться к нему. В этот раз в консоль попадает значение этого свойства — 1 . Если раскомментировать последнюю строку примера, то попытка выполнения этого кода приведёт к ошибке.
Ключевое слово const очень похоже на let , в частности, оно обладает блочной областью видимости.
В современных условиях вполне допустимо использовать для объявления всех сущностей, значения которых менять не планируется, ключевое слово const , прибегая к let только в особых случаях. Почему? Всё дело в том, что лучше всего стремиться к использованию как можно более простых из доступных конструкций для того, чтобы не усложнять программы и избегать ошибок.
Типы данных
JavaScript иногда называют «нетипизированным» языком, но это не соответствует реальному положению дел. В переменные, и правда, можно записывать значения разных типов, но типы данных в JavaScript, всё-таки, есть. В частности, речь идёт о примитивных и об объектных типах данных.
Для того чтобы определить тип данных некоего значения, можно воспользоваться оператором typeof . Он возвращает строку, указывающую тип операнда.
▍Примитивные типы данных
Вот список примитивных типов данных JavaScript:
- number (число)
- string (строка)
- boolean (логическое значение)
- null (специальное значение null )
- undefined (специальное значение undefined )
- symbol (символ, используется в особых случаях, появился в ES6)
Поговорим о наиболее часто используемых типах данных из этого списка.
Тип number
Значения типа number в JavaScript представлены в виде 64-битных чисел двойной точности с плавающей запятой.
В коде числовые литералы представлены в виде целых и дробных чисел в десятичной системе счисления. Для записи чисел можно использовать и другие способы. Например, если в начале числового литерала имеется префикс 0x — он воспринимается как число, записанное в шестнадцатеричной системе счисления. Числа можно записывать и в экспоненциальном представлении (в таких числах можно найти букву e ).
Вот примеры записи целых чисел:
10 5354576767321 0xCC // шестнадцатеричное число
Вот дробные числа.
3.14 .1234 5.2e4 //5.2 * 10^4
Числовые литералы (такое поведение характерно и для некоторых других примитивных типов), при попытке обращения к ним как к объектам, автоматически, на время выполнения операции, преобразуются в соответствующие объекты, которые называют «объектными обёртками». В данном случае речь идёт об объектной обёртке Number .
Вот, например, как выглядит попытка обратиться к переменной a , в которую записан числовой литерал, как к объекту, в консоли Google Chrome.

Подсказка по объектной обёртке Number
Если, например, воспользоваться методом toString() объекта типа Number , он возвратит строковое представление числа. Выглядит соответствующая команда, которую можно выполнить в консоли браузера (да и в обычном коде) так:
a.toString()
Обратите внимание на двойные скобки после имени метода. Если их не поставить, система не выдаст ошибку, но, вместо ожидаемого вывода, в консоли окажется нечто, совсем не похожее на строковое представление числа 5.
Глобальный объект Number можно использовать в виде конструктора, создавая с его помощью новые числа (правда, в таком виде его практически никогда не используют), им можно пользоваться и как самостоятельной сущностью, не создавая его экземпляры (то есть — некие числа, представляемые с его помощью). Например, его свойство Number.MAX_VALUE содержит максимальное числовое значение, представимое в JavaScript.
Тип string
Значения типа string представляют собой последовательности символов. Такие значения задают в виде строковых литералов, заключённых в одинарные или двойные кавычки.
'A string' "Another string"
Строковые значения можно разбивать на несколько частей, используя символ обратной косой черты (backslash).
"A \ string"
Строка может содержать так называемые escape-последовательности, интерпретируемые при выводе строки в консоль. Например, последовательность \n означает символ перевода строки. Символ обратной косой черты можно использовать и для того, чтобы добавлять кавычки в строки, заключённые в такие же кавычки. Экранирование символа кавычки с помощью \ приводит к тому, что система не воспринимает его как специальный символ.
'I\'m a developer'
Строки можно конкатенировать с использованием оператора + .
"A " + "string"
Шаблонные литералы
В ES2015 появились так называемые шаблонные литералы, или шаблонные строки. Они представляют собой строки, заключённые в обратные кавычки ( ` ) и обладают некоторыми интересными свойствами.
`a string`
Например, в шаблонные литералы можно подставлять некие значения, являющиеся результатом вычисления JavaScript-выражений.
`a string with $` `a string with $` `a string with $`
Использование обратных кавычек упрощает многострочную запись строковых литералов:
`a string with $`
Тип boolean
В JavaScript есть пара зарезервированных слов, использующихся при работе с логическими значениями — это true (истина), и false (ложь). Операции сравнения, например, такие, как == , === , < , >, возвращают true или false .
Логические выражения используются в конструкциях наподобие if и while , помогая управлять ходом выполнения программы.
При этом надо отметить, что там, где ожидается значение true или false , можно использовать и другие значения, которые автоматически расцениваются языком как истинные (truthy) или ложные (falsy).
В частности, ложными значениями являются следующие:
0 -0 NaN undefined null '' //пустая строка
Остальные значения являются истинными.
Тип null
В JavaScript имеется специальное значение null , которое указывает на отсутствие значения. Подобные значения используются и в других языках.
Тип undefined
Значение undefined , записанное в некую переменную, указывает на то, что эта переменная не инициализирована и значение для неё отсутствует.
Это значение автоматически возвращается из функций, результат работы которых не возвращается явно, с использованием ключевого слова return . Если функция принимает некий параметр, который, при её вызове, не указан, он также устанавливается в undefined .
Для того чтобы проверить значение на undefined , можно воспользоваться следующей конструкцией.
typeof variable === 'undefined'
▍Объекты
Все значения, не являющиеся примитивными, имеют объектный тип. Речь идёт о функциях, массивах, о том, что мы называем «объектами», и о многих других сущностях. В основе всех этих типов данных лежит тип object , и они, хотя и во многом друг от друга отличаются, имеют и много общего.
Выражения
Выражения — это фрагменты кода, которые можно обработать и получить на основе проведённых вычислений некое значение. В JavaScript существует несколько категорий выражений.
Арифметические выражения
В эту категорию попадают выражения, результатом вычисления которых являются числа.
1 / 2 i++ i -= 2 i * 2
Строковые выражения
Результатом вычисления таких выражений являются строки.
'A ' + 'string' 'A ' += 'string'
Первичные выражения
В эту категорию попадают литералы, константы, ссылки на идентификаторы.
2 0.02 'something' true false this //контекст выполнения, ссылка на текущий объект undefined i //где i является переменной или константой
Сюда же можно отнести и некоторые ключевые слова и конструкции JavaScript.
function class function* //генератор yield //команда приостановки/возобновления работы генератора yield* //делегирование другому итератору или генератору async function* //асинхронное функциональное выражение await //организация ожидания выполнения асинхронной функции /pattern/i //регулярное выражение () //группировка
Выражения инициализации массивов и объектов
[] //литерал массива <> //объектный литерал [1,2,3] >
Логические выражения
В логических выражениях используются логические операторы, результатом их вычисления оказываются логические значения.
a && b a || b !a
Выражения доступа к свойствам
Эти выражения позволяют обращаться к свойствам и методам объектов.
object.property //обращение к свойству (или методу) объекта object[property] object['property']
Выражения создания объектов
new object() new a(1) new MyRectangle('name', 2, )
Выражения объявления функций
function() <> function(a, b) < return a * b >(a, b) => a * b a => a * 2 () =>
Выражения вызова
Такие выражения используются для вызова функций или методов объектов.
a.x(2) window.resize()
Работа с объектами
Выше мы уже сталкивались с объектами, говоря об объектных литералах, о вызове их методов, о доступе к их свойствам. Здесь мы поговорим об объектах подробнее, в частности, рассмотрим механизм прототипного наследования и использование ключевого слова class .
▍Прототипное наследование
JavaScript выделяется среди современных языков программирования тем, что поддерживает прототипное наследование. Большинство же объектно-ориентированных языков используют модель наследования, основанную на классах.
У каждого JavaScript-объекта есть особое свойство ( __proto__ ), которое указывает на другой объект, являющийся его прототипом. Объект наследует свойства и методы прототипа.
Предположим, у нас имеется объект, созданный с помощью объектного литерала.
const car = <>
Или мы создали объект, воспользовавшись конструктором Object .
const car = new Object()
В любом из этих случаев прототипом объекта car будет Object.prototype .
Если создать массив, который тоже является объектом, его прототипом будет объект Array.prototype .
const list = [] //или так const list = new Array()
Проверить это можно следующим образом.
car.__proto__ == Object.prototype //true car.__proto__ == new Object().__proto__ //true list.__proto__ == Object.prototype //false list.__proto__ == Array.prototype //true list.__proto__ == new Array().__proto__ //true
Здесь мы пользовались свойством __proto__ , оно не обязательно должно быть доступно разработчику, но обычно обращаться к нему можно. Надо отметить, что более надёжным способом получить прототип объекта является использование метода getPrototypeOf() глобального объекта Object .
Object.getPrototypeOf(new Object())
Все свойства и методы прототипа доступны объекту, имеющему этот прототип. Вот, например, как выглядит их список для массива.

Подсказка по массиву
Базовым прототипом для всех объектов является Object.prototype .
Array.prototype.__proto__ == Object.prototype
У Object.prototype прототипа нет.
То, что мы видели выше, является примером цепочки прототипов.
При попытке обращения к свойству или методу объекта, если такого свойства или метода у самого объекта нет, их поиск выполняется в его прототипе, потом — в прототипе прототипа, и так — до тех пор, пока искомое будет найдено, или до тех пор, пока цепочка прототипов не кончится.
Помимо создания объектов с использованием оператора new и применения объектных литералов или литералов массивов, создать экземпляр объекта можно с помощью метода Object.create() . Первый аргумент, передаваемый этому методу, представляет собой объект, который станет прототипом создаваемого с его помощью объекта.
const car = Object.create(Object.prototype)
Проверить, входит ли некий объект в цепочку прототипов другого объекта, можно с использованием метода isPrototypeOf() .
const list = [] Array.prototype.isPrototypeOf(list)
Функции-конструкторы
Выше мы создавали новые объекты, пользуясь уже имеющимися в языке функциями-конструкторами (при их вызове используется ключевое слово new ). Такие функции можно создавать и самостоятельно. Рассмотрим пример.
function Person(name) < this.name = name >Person.prototype.hello = function() < console.log(this.name) >let person = new Person('Flavio') person.hello() console.log(Person.prototype.isPrototypeOf(person))
Здесь мы создаём функцию-конструктор. При её вызове создаётся новый объект, на который указывает ключевое слово this в теле конструктора. Мы добавляем в этот объект свойство name и записываем в него то, что передано конструктору. Этот объект возвращается из конструктора автоматически. С помощью функции-конструктора можно создать множество объектов, свойства name которых будут содержать то, что передано при их создании конструктору.
После создания конструктора мы добавляем в его прототип функцию, которая будет выводить в консоль значение свойства name объекта, созданного с помощью этой функции. Все объекты, созданные с помощью этого конструктора, будут иметь один и тот же прототип, а значит и пользоваться одной и той же функцией hello() . Это несложно проверить, создав ещё один объект типа Person и сравнив его функцию hello() с функцией уже имеющегося в примере объекта (имя функции в таком случае записывают без скобок).
▍Классы
В стандарте ES6 в JavaScript пришло такое понятие как «класс».
До этого в JavaScript можно было пользоваться лишь вышеописанным механизмом прототипного наследования. Этот механизм непривычно выглядел для программистов, пришедших в JS из других языков. Поэтому в языке и появились классы, которые, по сути, являются «синтаксическим сахаром» для прототипного механизма наследования. То есть, и объекты, созданные традиционным способом, и объекты, созданные с использованием классов, имеют прототипы.
Объявление класса
Вот как выглядит объявление класса.
class Person < constructor(name) < this.name = name >hello() < return 'Hello, I am ' + this.name + '.' >>
У класса есть идентификатор, который можно использовать для создания новых объектов с применением конструкции new ClassIdentifier() .
При создании нового объекта вызывается метод constructor , ему передаются параметры.
В классе можно объявлять методы. В нашем случае hello() — это метод, который могут вызывать все объекты, созданные на основе класса. Вот как выглядит создание нового объекта с использованием класса Person .
const flavio = new Person('Flavio') flavio.hello()
Наследование, основанное на классах
Классы могут расширять другие классы. Объекты, созданные на основе таких классов, будут наследовать и методы исходного класса, и методы, заданные в расширенном классе.
Если класс, расширяющий другой класс (наследник этого класса) имеет метод, имя которого совпадает с тем, который есть у класса-родителя, этот метод имеет преимущество перед исходным.
class Programmer extends Person < hello() < return super.hello() + ' I am a programmer.' >> const flavio = new Programmer('Flavio') flavio.hello()
При вызове метода hello() в вышеприведённом примере будет возвращена строка Hello, I am Flavio. I am a programmer .
В классах не предусмотрено наличие переменных (свойств), свойства создаваемых с помощью классов объектов нужно настраивать в конструкторе.
Внутри класса можно обращаться к родительскому классу с использованием ключевого слова super .
Статические методы
Методы, описываемые в классе, можно вызывать, обращаясь к объектам, созданным на основе этого класса, но не к самому классу. Статические ( static ) методы можно вызывать, обращаясь непосредственно к классу.
Приватные методы
В JavaScript нет встроенного механизма, который позволяет объявлять приватные (частные, закрытые) методы. Это ограничение можно обойти, например, с использованием замыканий.
Геттеры и сеттеры
В классе можно описывать методы, предваряя их ключевыми словами get или set . Это позволяет создавать так называемые геттеры и сеттеры — функции, которые используются для управления доступом к свойствам объектов, созданных на основе класса. Геттер вызывается при попытке чтения значения псевдо-свойства, а сеттер — при попытке записи в него нового значения.
class Person < constructor(name) < this.userName = name >set name(value) < this.userName = value >get name() < return this.userName >>
Итоги
В этом материале мы поговорили о переменных, о типах данных, о выражениях и о работе с объектами в JavaScript. Темой нашего следующего материала будут функции.
Уважаемые читатели! Если вы уже давно пишете на JS, просим рассказать о том, как вы относитесь к появлению в языке ключевого слова class.

- Блог компании RUVDS.com
- Веб-разработка
- JavaScript
Конспект по JS темам для middle разработчика
Основное отличие между let и var в ECMAScript 6 заключается в области видимости переменных.
Ключевое слово var используется для объявления переменной в функциональной области видимости, а также в глобальной области видимости. При использовании var переменная будет доступна в любой части функции или в любом месте кода за пределами функции.
Ключевое слово let, с другой стороны, используется для объявления переменной в блочной области видимости, которая ограничивается фигурными скобками, например, в циклах или блоках условных операторов. Это означает, что переменная объявленная с помощью let будет видна только внутри блока, в котором она была объявлена.
Также важно отметить, что переменная, объявленная с помощью let, не может быть повторно объявлена в той же области видимости, в отличие от переменной, объявленной с помощью var.
Вот пример, который демонстрирует разницу между let и var:
function example() < var x = 1; if (true) < var x = 2; console.log(x); // 2 >console.log(x); // 2 > function example2() < let y = 1; if (true) < let y = 2; console.log(y); // 2 >console.log(y); // 1 >
В функции example переменная x объявлена с помощью var, поэтому ее значение изменяется и доступно внутри всей функции.
В функции example2 переменная y объявлена с помощью let, поэтому ее значение доступно только внутри блока, в котором она была объявлена. Когда код выходит из блока, значение переменной y снова становится равным 1.
Ключевое слово const
В ECMAScript 6 используется для объявления переменных, которые нельзя переназначить. То есть, когда переменная объявлена с помощью const, ее значение не может быть изменено после ее инициализации.
В отличие от let или var, переменные, объявленные с помощью const, не могут быть переназначены. Это означает, что после инициализации значения переменной с помощью const, вы не можете изменить это значение. Кроме того, переменная, объявленная с помощью const, должна быть инициализирована в момент ее объявления.
const PI = 3.14159265359; PI = 3.14; // TypeError: Assignment to constant variable.
В этом примере переменная PI объявлена с помощью const и ей присвоено значение числа Пи. Попытка изменить значение PI приведет к ошибке типа “TypeError: Assignment to constant variable”.
Также важно отметить, что переменная, объявленная с помощью const, не может быть повторно объявлена в той же области видимости, и не может иметь того же имени, что и другая переменная, объявленная с помощью let или var.
Использование const предпочтительно в тех случаях, когда вы хотите создать переменную, значение которой не будет изменяться в процессе выполнения программы, например, для объявления констант, таких как значения математических констант, кодов ошибок и других значений, которые не должны меняться.
Стрелочные функции (arrow functions)
Являются новой конструкцией в ECMAScript 6 и предоставляют более короткий и удобный синтаксис для определения функций.
Особенности стрелочных функций:
- Короткий синтаксис: Стрелочные функции имеют более короткий синтаксис, чем обычные функции, что делает код более понятным и удобным.
- Не имеют своего контекста: В отличие от обычных функций, у которых есть свой контекст (this), у стрелочных функций нет своего контекста, что позволяет избежать некоторых ошибок и упрощает работу с контекстом.
- Не могут быть вызваны с new: Стрелочные функции не могут быть вызваны с оператором new, поскольку у них нет своего контекста и не могут создавать новый объект.
- Не могут использоваться для определения методов объекта: Стрелочные функции не могут использоваться для определения методов объекта, поскольку у них нет своего контекста и this будет ссылаться на глобальный объект.
- Не имеют своих собственных аргументов: Стрелочные функции не имеют своих собственных аргументов, а используют аргументы из родительской области видимости.
- Не могут быть именованы: Стрелочные функции не могут быть именованы и должны быть присвоены переменной или использованы как анонимная функция.
const sum = (a, b) => a + b; console.log(sum(2, 3));
Использование функции внутри метода массива:
const numbers = [1, 2, 3]; const squared = numbers.map(num => num ** 2); console.log(squared);
Классы (classes)
Классы в JavaScript появились в ECMAScript 6 и представляют собой синтаксический сахар над прототипным наследованием. Классы позволяют создавать объекты с заданными свойствами и методами, а также использовать наследование для создания новых классов на основе существующих.
class MyClass < constructor() < // конструктор класса >myMethod() < // метод класса >>
В примере выше определен класс MyClass, который имеет конструктор и метод myMethod. Конструктор выполняется при создании нового объекта на основе класса, а методы доступны всем объектам этого класса.
Создание объекта на основе класса:
const myObject = new MyClass();
В примере выше создается новый объект myObject на основе класса MyClass.
Наследование классов
class ChildClass extends ParentClass < constructor() < super(); // конструктор класса наследника >childMethod() < // метод класса наследника >>
В примере выше класс ChildClass наследует свойства и методы класса ParentClass. Конструктор класса наследника вызывает конструктор класса-родителя с помощью ключевого слова super. Класс наследника может добавлять свои собственные методы и свойства.
Создание объекта на основе класса-наследника
const myChildObject = new ChildClass();
В примере выше создается новый объект myChildObject на основе класса ChildClass.
Классы предоставляют более удобный способ создания объектов с методами и свойствами, чем использование прототипов напрямую. Они также предоставляют механизм наследования для создания иерархии классов.
Rest и Spread операторы
Rest и Spread операторы – это новые возможности в JavaScript, которые появились в ECMAScript 6 и позволяют более гибко работать с массивами и объектами.
Rest оператор
Rest оператор используется для передачи оставшихся аргументов функции в виде массива. Он обозначается тремя точками … и следует за последним аргументом функции.
function myFunction(first, second, . others) < console.log("First:", first); console.log("Second:", second); console.log("Others:", others); >myFunction(1, 2, 3, 4, 5); // Output: // First: 1 // Second: 2 // Others: [3, 4, 5]
В примере выше функция myFunction принимает первые два аргумента (first и second) и остальные аргументы собирает в массив others с помощью оператора …. При вызове функции с аргументами 1, 2, 3, 4, 5, первые два аргумента будут присвоены переменным first и second, а остальные аргументы будут собраны в массив others.
Spread оператор
Spread оператор используется для распаковки массивов и объектов, т.е. он превращает массив или объект в список отдельных значений. Он также обозначается тремя точками …
const myArray = [1, 2, 3]; const newArray = [. myArray, 4, 5]; console.log(newArray); // Output: [1, 2, 3, 4, 5]
В примере выше массив myArray распаковывается с помощью оператора … и добавляются два новых элемента 4 и 5. Получившийся массив записывается в переменную newArray.
const myObject = < a: 1, b: 2, c: 3 >; const newObject = < . myObject, d: 4, e: 5 >; console.log(newObject); // Output:
В примере выше объект myObject распаковывается с помощью оператора … и добавляются два новых свойства d и e. Получившийся объект записывается в переменную newObject.
Rest и Spread операторы позволяют удобно работать с массивами и объектами и улучшают читаемость и понимание кода.
Деструктуризация (destructuring)
Позволяет извлекать значения из объектов и массивов и присваивать их переменным.
Извлечение значений из объекта:
const user = < name: 'John', age: 30 >; const < name, age >= user; console.log(name, age); // Output: 'John', 30
Извлечение значений из массива:
const numbers = [1, 2, 3]; const [a, b, c] = numbers; console.log(a, b, c);
Шаблонные строки (template strings)
Улучшенный способ создания строковых литералов начиная со спецификации ES6.
Шаблонные строки позволяют вставлять переменные и выражения в строку без использования оператора конкатенации +.
Шаблонные строки обозначаются обратными кавычками (“`) и могут содержать выражения, заключенные в фигурные скобки <> перед которыми указывается оператор ‘знак долара’ $.
const name = "John"; const age = 30; const message = `My name is $ and I'm $ years old.`; console.log(message); // Output: "My name is John and I'm 30 years old."
В примере выше создается строка message, которая содержит значения переменных name и age, вставленные в строку с помощью фигурных скобок и оператора $<>.
Шаблонные строки также могут использоваться для многострочного текста, без необходимости использования символов переноса строки (\n).
const message = ` Hello, World! `; console.log(message); // Output: "Hello,\nWorld!"
Шаблонные строки – это удобный и читаемый способ работы со строками в JavaScript, особенно при необходимости вставлять переменные и выражения.
Тип данных BigInt
Новый тип данных, добавленный в ECMAScript 2020 (ES2020), который позволяет представлять целые числа произвольной длины и точности.
В отличие от стандартного числового типа данных number, BigInt может представлять числа больше, чем максимальное значение типа number (2^53 – 1). BigInt поддерживает все стандартные математические операции, такие как сложение, вычитание, умножение и деление.
Для создания значения BigInt используется суффикс n, добавленный к числу.
const bigNumber = 1234567890123456789012345678901234567890n; console.log(bigNumber); // Output: 1234567890123456789012345678901234567890n
BigInt также поддерживает преобразование из других типов данных, таких как строка и number.
const str = "1234567890123456789012345678901234567890"; const bigNumberFromString = BigInt(str); console.log(bigNumberFromString); // Output: 1234567890123456789012345678901234567890n const number = 1234567890123456789012345678901234567890; const bigNumberFromNumber = BigInt(number); console.log(bigNumberFromNumber); // Output: 1234567890123456789012345678901234567890n
Поддерживает сравнение с другими значениями BigInt.
const bigNumber1 = 100n; const bigNumber2 = 200n; console.log(bigNumber1 < bigNumber2); // Output: true console.log(bigNumber1 === bigNumber2); // Output: false
Важно отметить, что BigInt и number – это разные типы данных, и нельзя выполнять операции между ними напрямую. Если необходимо производить операции между значениями BigInt и number, необходимо явно преобразовывать типы данных.
Тип данных Symbol
Является примитивным типом данных, который представляет уникальный идентификатор. Он был введен в ECMAScript 6 как способ создания не перечисляемых свойств в объектах.
Символ создается с помощью функции Symbol(), и каждое значение Symbol уникально.
Символы могут использоваться в качестве ключей свойств в объектах. При использовании символа в качестве ключа свойства он создает не перечисляемое свойство, которое нельзя получить с помощью обычных методов доступа к свойствам объекта (например, точечной нотации или квадратных скобок).
Символы могут иметь необязательный строковый аргумент, который предоставляет описание символа. Это описание главным образом полезно для отладки.
Символы могут быть использованы в качестве ключей в объектах WeakMap, который является особенным типом объектов, где ключи должны быть объектами и не сохраняются в памяти, если на них нет ссылок.
Объект Math
Представляет математические функции и константы. Перечень наиболее часто используемых функций и констант, доступных в объекте Math:
- Math.PI: представляет число π (3.141592653589793)
- Math.abs(x): возвращает абсолютное значение числа x
- Math.ceil(x): округляет число x в большую сторону до ближайшего целого числа
- Math.floor(x): округляет число x в меньшую сторону до ближайшего целого числа
- Math.round(x): округляет число x до ближайшего целого числа
- Math.max(x1, x2, …, xn): возвращает наибольшее из переданных чисел x1, x2, …, xn
- Math.min(x1, x2, …, xn): возвращает наименьшее из переданных чисел x1, x2, …, xn
- Math.pow(x, y): возвращает значение числа x в степени y
- Math.sqrt(x): возвращает квадратный корень числа x
- Math.random(): возвращает случайное число между 0 и 1
- Math.sin(x): возвращает синус угла x
- Math.cos(x): возвращает косинус угла x
- Math.tan(x): возвращает тангенс угла x
- Math.log(x): возвращает натуральный логарифм числа x
- Math.exp(x): возвращает экспоненту числа x
const x = 5; const y = -3; console.log(Math.abs(y)); // 3 console.log(Math.ceil(x)); // 5 console.log(Math.floor(x)); // 5 console.log(Math.max(x, y)); // 5 console.log(Math.min(x, y)); // -3 console.log(Math.pow(x, 2)); // 25 console.log(Math.sqrt(x)); // 2.23606797749979 console.log(Math.random()); // случайное число между 0 и 1
NaN
В JavaScript NaN означает “Not a Number” (не число) и является значением, которое представляет результат математической операции, которую невозможно выполнить. Например, если мы попытаемся выполнить математическую операцию, используя некорректные аргументы, результатом будет значение NaN.
Например, попытка выполнить деление на ноль:
const result = 1 / 0; // Infinity const notANumber = 0 / 0; // NaN
NaN используется, чтобы указать, что результат операции не является числом, например, при попытке выполнить математические операции с нечисловыми значениями:
const notANumber1 = 10 / "hello"; // NaN const notANumber2 = Math.sqrt(-1); // NaN
Для проверки, является ли значение NaN, можно использовать функцию isNaN():
console.log(isNaN(notANumber1)); // true console.log(isNaN(notANumber2)); // true console.log(isNaN(10)); // false console.log(isNaN(notANumber1)); // true console.log(isNaN(notANumber2)); // true console.log(isNaN(10)); // false
Заметьте, что функция isNaN() возвращает true также в случае, если аргументом является нечисловое значение, поэтому перед использованием её нужно привести аргумент к числу, например, с помощью функции parseFloat() или parseInt().
Object methods
В JavaScript объект представляет собой коллекцию свойств, каждое из которых имеет имя (строка) и значение, которое может быть любым типом данных, включая другой объект.
Вот несколько методов объекта в JavaScript:
- Object.keys(obj) – возвращает массив строк, представляющих имена перечисляемых свойств объекта obj.
- Object.values(obj) – возвращает массив значений перечисляемых свойств объекта obj.
- Object.entries(obj) – возвращает массив массивов, где каждый подмассив содержит пару “ключ-значение” для каждого перечисляемого свойства объекта obj.
- Object.assign(target, …sources) – копирует значения всех перечисляемых свойств объекта source в объект target, затирая при этом существующие свойства с тем же именем. Возвращает объект target.
- Object.freeze(obj) – замораживает объект obj, делая его свойства неизменяемыми.
- Object.seal(obj) – запечатывает объект obj, предотвращая добавление новых свойств и удаление существующих. Существующие свойства можно изменять.
- Object.hasOwnProperty(prop) – возвращает логическое значение, указывающее, содержит ли объект obj свойство с именем prop.
- Object.is(value1, value2) – возвращает логическое значение, указывающее, равны ли value1 и value2. Этот метод использует тот же алгоритм сравнения, что и оператор ===.
- Object.fromEntries(entries) – создает новый объект, используя массив entries в качестве источника пар “ключ-значение”. Каждый элемент массива entries должен быть подмассивом длиной 2, содержащим ключ и соответствующее ему значение.
Директива “use strict”
Это директива (строка, начинающаяся с “use strict”), которая включает строгий режим (strict mode) в JavaScript. Строгий режим представляет собой набор ограничений, которые обеспечивают более безопасное и четкое выполнение JavaScript кода. Эта директива может быть использована в начале скрипта, функции или блока кода.
Строгий режим включает следующие ограничения:
- Запрет на использование необъявленных переменных.
- Запрет на удаление свойств, которые не могут быть удалены.
- Запрет на переопределение свойств, которые не могут быть переопределены.
- Запрет на использование ключевых слов в качестве идентификаторов.
- Запрет на использование функций в качестве конструкторов без использования ключевого слова “new”.
- Запрет на использование функций с одинаковыми именами в одной области видимости.
- Запрет на изменение значений некоторых встроенных объектов, таких как Object, Array, Math.
- Использование директивы “use strict” позволяет разработчикам избегать ошибок и неоднозначностей в своем коде, делая его более читаемым, надежным и безопасным.
Scope
Поднятие (hoisting)
В JavaScript термин “hoisting” означает поведение, при котором объявления переменных и функций перемещаются вверх их соответствующих областей видимости движком JavaScript во время фазы компиляции, до выполнения кода. Это означает, что независимо от того, где в коде были объявлены переменные и функции, они будут доступны во всей области видимости.
function example() < console.log(a); // undefined var a = 1; console.log(a); // 1 >example();
В этом примере переменная a объявлена после первого console.log(), но до ее присваивания значения 1. Однако благодаря hoisting, объявление переменной a будет перемещено в начало области видимости функции example(), и первый console.log() выведет undefined, потому что переменная еще не была присвоена значению. Второй console.log() выведет 1, потому что значение было присвоено переменной a во время выполнения функции.
Лексическая область видимости (lexical scoping)
Один из способов управления областью видимости переменных в программировании. В лексической области видимости, доступность переменной определяется на основе ее расположения в исходном коде программы.
В JavaScript лексическая область видимости реализуется через использование замыканий (closures). Замыкание создается, когда функция объявляется внутри другой функции, и вложенная функция сохраняет доступ к переменным своей внешней функции, даже после того, как внешняя функция завершила свое выполнение.
function outerFunction() < var outerVar = "I'm in the outer function!"; function innerFunction() < var innerVar = "I'm in the inner function!"; console.log(innerVar); console.log(outerVar); >innerFunction(); > outerFunction();
В этом примере функция innerFunction() находится внутри функции outerFunction(). Это означает, что innerFunction() имеет доступ к переменной outerVar, объявленной внутри outerFunction(), благодаря лексической области видимости. Когда мы вызываем innerFunction() внутри outerFunction(), она выводит значения обеих переменных, и выводит:
I'm in the inner function! I'm in the outer function!
Сборка мусора (garbage collection)
Процесс автоматического поиска и удаления неиспользуемых объектов из памяти компьютера.
В языках программирования, таких как JavaScript, объекты создаются динамически во время выполнения программы, и их память должна быть выделена и освобождена явно. Если объект больше не используется в программе, но память, выделенная для его хранения, не была освобождена, это может привести к утечкам памяти и замедлению работы программы.
Сборка мусора происходит автоматически в JavaScript и некоторых других языках программирования, без явного управления программистом. JavaScript-движок периодически проверяет все объекты в памяти и удаляет те, на которые нет ссылок из активного кода. Ссылка – это переменная или другой объект, который указывает на объект в памяти. Если объект больше не имеет ссылок на себя, он считается неиспользуемым и может быть удален из памяти.
JavaScript не предоставляет явного управления памятью, так как сборка мусора является автоматическим процессом, выполняемым в фоновом режиме внутри JavaScript-движка. Однако есть несколько способов, которыми программист может оптимизировать использование памяти в своем коде:
- Использование let и const вместо var – объявление переменных с помощью let и const позволяет ограничить область их видимости, что позволяет JavaScript-движку освобождать память, выделенную для этих переменных, как только они выходят за пределы своей области видимости.
- Уменьшение использования глобальных переменных – глобальные переменные могут занимать больше памяти, чем локальные переменные, поэтому программист должен стремиться к уменьшению их использования.
- Определение переменных в начале функции – определение всех переменных в начале функции позволяет снизить вероятность утечек памяти и повысить читаемость кода.
- Очистка ссылок на объекты – когда объект больше не нужен в программе, ссылки на него должны быть удалены. Это может быть достигнуто путем присвоения переменной значения null или undefined.
- Использование методов массивов, таких как splice() и slice(), вместо операторов delete и конкатенации массивов – это может сократить использование памяти.
- Использование кэширования и мемоизации для предотвращения повторного вычисления данных и уменьшения использования памяти.
Мемоизация (memoization) в JavaScript – это оптимизация, которая заключается в сохранении результатов выполнения функций для предотвращения повторного вычисления этих результатов в будущем.
Идея мемоизации заключается в том, что при вызове функции с определенными аргументами результат может быть кэширован в памяти, чтобы при следующем вызове функции с теми же самыми аргументами, результат можно было получить непосредственно из кэша без повторного выполнения функции.
Мемоизация может быть полезна для ускорения работы функций, которые имеют дорогостоящие вычисления или обращения к базе данных. Примером может служить функция расчета факториала:
function factorial(n) < if (n === 0 || n === 1) < return 1; >else < return n * factorial(n - 1); >>
При больших значениях n эта функция может занимать много времени на выполнение, так как она вычисляет факториал каждого числа заново. Можно оптимизировать эту функцию, используя мемоизацию:
function factorial(n, cache = <>) < if (n === 0 || n === 1) < return 1; >else if (cache[n]) < return cache[n]; >else < const result = n * factorial(n - 1, cache); cache[n] = result; return result; >>
Здесь мы используем дополнительный параметр cache, который хранит результаты выполнения функции для каждого значения n. Если значение уже находится в кэше, мы возвращаем его, а если нет, то вычисляем и сохраняем результат в кэше для последующего использования.
Мемоизация может быть реализована различными способами в JavaScript, например, с помощью замыканий, объектов или декораторов.
В целом, хороший стиль программирования и правильное использование языка помогут избежать утечек памяти и оптимизировать использование памяти в JavaScript.
Объектно-ориентированный подход в javascript (OOP)
constructors vs factories
В JavaScript конструкторы и фабрики (constructors and factories) используются для создания объектов, но они имеют некоторые различия в своей реализации и использовании.
Конструкторы – это функции, которые используются для создания новых объектов. Они используют оператор new и имеют имена, начинающиеся с большой буквы, например:
function Person(name, age)
Конструкторы создают новый объект this и добавляют свойства и методы в этот объект. Затем они возвращают этот объект по умолчанию, если не был указан другой объект в качестве возвращаемого значения.
Фабрики – это функции, которые также используются для создания объектов, но они возвращают новый объект, не используя оператор new и не создавая конструкторы. Фабрики могут использовать любые другие функции и методы JavaScript для создания объектов. Пример:
function createPerson(name, age) < return < name: name, age: age, sayHello: function() < console.log('Hello, my name is ' + this.name); >>; >
Фабрики могут использовать любые другие функции и методы JavaScript для создания объектов, а также могут возвращать различные типы объектов в зависимости от входных данных и условий.
Конструкторы и фабрики имеют различия в том, как они создают и возвращают объекты. Конструкторы создают объект с помощью new, добавляют свойства и методы непосредственно в объект this и возвращают его по умолчанию. Фабрики возвращают новый объект, используя любые другие функции и методы JavaScript для создания объектов, а также могут возвращать различные типы объектов в зависимости от входных данных и условий.
Какой из этих подходов использовать – зависит от задачи, которую вы пытаетесь решить. Если вы хотите создать объекты с помощью новых конструкторов и добавить им методы и свойства непосредственно в объект this, то конструкторы – подходящий выбор. Если же вы хотите создавать объекты с помощью функций и методов JavaScript и возвращать различные типы объектов в зависимости от входных данных и условий, то фабрики могут быть более подходящим выбором.
Inheritance (наследование)
Наследование в JavaScript – это механизм, с помощью которого один объект может унаследовать свойства и методы от другого объекта. Оно достигается через цепочку прототипов, где объекты наследуют свойства и методы от своих прототипов.
Для создания наследования в JavaScript используется ключевое слово extends. С помощью него можно создать класс-наследник, который будет наследовать свойства и методы от родительского класса. Например:
class Animal < constructor(name) < this.name = name; >speak() < console.log(`$makes a noise.`); > > class Dog extends Animal < speak() < console.log(`$barks.`); > > const d = new Dog('Mitzie'); d.speak(); // Mitzie barks.
В этом примере класс Dog является наследником класса Animal, и наследует его свойство name и метод speak(). Однако, метод speak() переопределен в классе Dog, и теперь при вызове метода speak() у объекта класса Dog, выводится сообщение “Mitzie barks.”.
Также можно создать объект, который будет наследовать свойства и методы от другого объекта с помощью метода Object.create(). Например:
const animal = < name: '', speak() < console.log(`$makes a noise.`); > >; const dog = Object.create(animal); dog.name = 'Mitzie'; dog.speak(); // Mitzie makes a noise.
В этом примере объект dog наследует свойство name и метод speak() от объекта animal, который был передан методу Object.create().
Прототипное наследование
Прототипное наследование в JavaScript – это механизм наследования, который основывается на использовании прототипов объектов. Каждый объект в JavaScript имеет свойство __proto__, которое указывает на его прототип. Прототип объекта может быть другим объектом, который будет использоваться для наследования свойств и методов.
В прототипном наследовании, если свойство или метод не найдены в самом объекте, JavaScript будет искать их в прототипе объекта, затем в прототипе прототипа и так далее, пока не будет найден искомый элемент или не будет достигнут конец цепочки прототипов.
Например, допустим, у нас есть объект animal, который имеет свойство name и метод speak():
const animal = < name: '', speak() < console.log(`$makes a noise.`); > >;
Мы можем создать новый объект dog, который будет наследовать свойства и методы объекта animal:
const dog = < name: 'Mitzie' >; dog.__proto__ = animal; dog.speak(); // Mitzie makes a noise.
В этом примере мы устанавливаем прототип объекта dog на объект animal, используя свойство __proto__. Теперь, когда мы вызываем метод speak() у объекта dog, JavaScript сначала ищет этот метод в самом объекте dog. Но поскольку метод не найден, JavaScript продолжает поиск в прототипе объекта dog, который является объектом animal. В итоге метод speak() находится в объекте animal, и выводится сообщение “Mitzie makes a noise.”.
Прототипное наследование также используется в классах, которые были введены в ECMAScript 2015. В классах используется ключевое слово extends, которое указывает, какой класс будет являться прототипом для наследования свойств и методов.
Наследование vs композиция
Наследование и композиция – это два основных концепта объектно-ориентированного программирования.
Наследование – это механизм, который позволяет создавать новый класс на основе существующего класса. Новый класс, известный как подкласс, наследует все свойства и методы существующего класса, известного как суперкласс. Подкласс может добавлять новые свойства и методы или переопределять существующие. Наследование – это способ повторного использования кода и создания более специализированных классов из существующих.
Композиция, с другой стороны, – это механизм, который позволяет объектам состоять из других объектов. Другими словами, объект может содержать один или несколько объектов других классов. Это позволяет создавать более гибкий и модульный код, поскольку объекты могут быть комбинированы различными способами для создания разных систем.
Пример композиции в JavaScript может выглядеть так:
Предположим, у нас есть класс “Машина”, который имеет свойства, такие как “марка”, “модель” и “год выпуска”. Теперь мы хотим создать класс “Водитель”, который имеет свойства “имя”, “возраст” и “опыт вождения”.
Вместо того, чтобы наследовать свойства “марка”, “модель” и “год выпуска” из класса “Машина” в класс “Водитель”, мы можем использовать композицию и создать объект “Машина” внутри объекта “Водитель”. Это означает, что объект “Водитель” будет содержать объект “Машина” как одно из своих свойств.
Код может выглядеть примерно так:
class Car < constructor(make, model, year) < this.make = make; this.model = model; this.year = year; >> class Driver < constructor(name, age, experience, car) < this.name = name; this.age = age; this.experience = experience; this.car = car; >> let car = new Car("Toyota", "Camry", 2022); let driver = new Driver("John", 30, 5, car);
В этом примере, объект “driver” содержит объект “car” в своем свойстве “car”. Таким образом, мы можем получить доступ к свойствам “марка”, “модель” и “год выпуска” машины, используя свойство “car” объекта “driver”, например:
console.log(driver.car.make); // выведет "Toyota"
Одна из главных проблем, которую композиция решает по сравнению с наследованием в JavaScript, связана с тем, что наследование может привести к созданию очень глубокой иерархии классов. Это может привести к тому, что код становится трудным для понимания и поддержки.
Кроме того, наследование приводит к тому, что подкласс наследует все свойства и методы суперкласса, даже те, которые ему не нужны. Это может привести к перегруженности объектов и затратам на ресурсы.
Композиция же позволяет создавать объекты, используя уже существующие объекты и комбинируя их свойства и методы. Таким образом, мы можем создавать более гибкие и легко поддерживаемые объекты, состоящие из более мелких и простых частей. Кроме того, композиция позволяет объектам использовать только те свойства и методы, которые им необходимы, что повышает их эффективность и экономит ресурсы.
В целом, композиция позволяет создавать более гибкие, модульные и легко поддерживаемые объекты, чем наследование. Композиция также является более гибким подходом, который позволяет создавать объекты, которые лучше отвечают специфическим потребностям приложения.
Парадигмы программирования в javascript
JavaScript имеет несколько парадигм программирования, помимо объектно-ориентированного программирования (ООП), включая следующие:
- Функциональное программирование: Эта парадигма предполагает использование функций как основной единицы кода, которые могут быть переданы в другие функции и возвращать другие функции в качестве результата. Функциональное программирование обычно используется для создания высокоуровневых и абстрактных функций, которые могут быть повторно использованы в различных частях программы.
- Реактивное программирование: Эта парадигма использует потоки данных и событий для обработки изменений в программе. Реактивное программирование используется для создания реактивных интерфейсов, где изменения пользовательского ввода мгновенно обновляют соответствующие части интерфейса.
- Асинхронное программирование: JavaScript поддерживает асинхронную обработку кода, где выполнение кода может быть приостановлено, пока ожидается ответ от внешнего API или другой операции. Это делает возможным использование колбэков, обратных вызовов и промисов для управления асинхронным кодом.
- Модульное программирование: JavaScript позволяет разделять код на модули, что делает его более организованным и управляемым. Это позволяет разработчикам создавать переиспользуемый код и избегать конфликтов имен и других проблем, связанных с глобальными переменными.
- Императивное программирование: Эта парадигма программирования подразумевает написание кода, который описывает, как программа должна выполняться, а не то, что она должна делать. Это стиль программирования, в котором определенная последовательность инструкций изменяет состояние программы.
- Декларативное программирование в JavaScript предполагает описание того, что нужно сделать, а не как это делать. В отличие от императивного программирования, где разработчик определяет каждый шаг выполнения программы, декларативное программирование подразумевает определение желаемого результата и использование готовых абстракций для достижения этого результата.
Рекурсия
Рекурсия является одной из фундаментальных концепций функционального программирования, и она широко используется в JavaScript для решения различных задач.
Рекурсия – это процесс, при котором функция вызывает саму себя с некоторыми изменениями параметров. В функциональном программировании рекурсия используется для итерации по структурам данных, таких как списки, деревья и графы.
Вот пример рекурсивной функции на JavaScript, которая вычисляет факториал числа:
function factorial(n) < if (n === 0 || n === 1) < return 1; >else < return n * factorial(n - 1); >>
В этом примере, если переданное число равно 0 или 1, функция возвращает 1. В противном случае она вызывает саму себя с аргументом n – 1 и умножает результат на n. Таким образом, при каждом вызове функции factorial аргумент n уменьшается на 1, пока не достигнет 0 или 1.
Рекурсия также может быть использована для обхода и поиска элементов в деревьях и графах, а также для сортировки массивов и других структур данных.
Однако, при использовании рекурсии необходимо быть осторожным, чтобы избежать бесконечной рекурсии, которая может привести к переполнению стека вызовов и сбою программы.
Функции map/reduce
Функции map и reduce являются часто используемыми в JavaScript функциями высшего порядка, которые позволяют работать с массивами более эффективно и удобно.
Функция map позволяет применить указанную функцию к каждому элементу массива и получить новый массив с результатами этой операции. Вот пример использования map для возведения в квадрат каждого элемента массива:
const arr = [1, 2, 3, 4, 5]; const squaredArr = arr.map((num) => num * num); console.log(squaredArr); // [1, 4, 9, 16, 25]
В этом примере мы передаем функцию, которая принимает каждый элемент массива и возвращает его квадрат, в качестве аргумента для функции map. Результатом выполнения map является новый массив, содержащий квадраты исходных элементов.
Функция reduce используется для агрегации значений в массиве и получения единственного результата. Она принимает два аргумента: функцию-аккумулятор и начальное значение. Вот пример использования reduce для нахождения суммы элементов массива:
const arr = [1, 2, 3, 4, 5]; const sum = arr.reduce((acc, num) => acc + num, 0); console.log(sum); // 15
В этом примере мы передаем функцию, которая принимает аккумулятор и текущий элемент массива, складывает их и возвращает новое значение аккумулятора, в качестве первого аргумента для функции reduce. Вторым аргументом мы передаем начальное значение аккумулятора, равное 0. Результатом выполнения reduce является сумма всех элементов массива.
Функции map и reduce могут быть объединены в цепочки, чтобы выполнить сложные операции на массивах, такие как фильтрация, манипуляции и агрегация данных. Кроме того, они могут быть использованы с функциональными конструкциями, такими как стрелочные функции и лямбда-выражения, для более лаконичного кода.
Mutable/immutable
В программировании существует два типа структур данных: изменяемые (mutable) и неизменяемые (immutable).
Изменяемые структуры данных – это те, которые могут быть изменены после создания. Примерами изменяемых структур данных в JavaScript являются объекты и массивы. При изменении изменяемой структуры данных, её исходное состояние также меняется.
let arr = [1, 2, 3]; arr.push(4); console.log(arr); // [1, 2, 3, 4]
В этом примере мы создаем массив и добавляем в него новый элемент с помощью метода push. Это изменяет исходный массив, добавляя новый элемент в его конец.
Неизменяемые структуры данных – это те, которые не могут быть изменены после создания. Примерами неизменяемых структур данных в JavaScript являются строки и числа. При изменении неизменяемой структуры данных, создается новая структура данных с новым значением.
let str = "Hello, world!"; let newStr = str.replace("world", "JavaScript"); console.log(str); // "Hello, world!" console.log(newStr); // "Hello, JavaScript!"
В этом примере мы создаем строку и затем создаем новую строку, заменяя слово “world” на “JavaScript”. Исходная строка не меняется, а создается новая строка с новым значением.
Неизменяемые структуры данных являются предпочтительными в функциональном программировании, так как они помогают избежать ошибок и упрощают проектирование программ. В JavaScript также существуют неизменяемые структуры данных, такие как Map и Set, которые позволяют работать с данными без изменения их исходного состояния.
Чистая функция (pure function)
Чистая функция (pure function) – это функция, которая при одинаковых входных аргументах всегда возвращает одинаковый результат и не имеет побочных эффектов на программу. Другими словами, чистая функция не изменяет состояние программы и не зависит от состояния программы в момент ее вызова.
Пример чистой функции:
function add(a, b)
В этом примере функция add принимает два аргумента a и b и возвращает их сумму. Эта функция всегда будет возвращать одинаковый результат при одинаковых аргументах, и не изменяет состояние программы.
Пример нечистой функции:
let count = 0; function increment()
В этом примере функция increment увеличивает значение переменной count на 1 и возвращает новое значение count. Эта функция изменяет состояние программы, поэтому она не является чистой функцией.
Использование чистых функций в функциональном программировании имеет несколько преимуществ, таких как упрощение тестирования и повышение читаемости и понятности кода. Кроме того, чистые функции являются безопасными для использования в многопоточных или параллельных системах, где изменение состояния программы может привести к ошибкам и непредсказуемому поведению.
Promises
Асинхронное программирование в JavaScript осуществляется с помощью промисов (promises), которые представляют собой обещание (promise) выполнения операции в будущем. Промисы позволяют отложить выполнение операции до тех пор, пока не будут выполнены определенные условия, например, завершение загрузки данных или выполнение других операций.
Промисы представлены классом Promise, который имеет два состояния: pending (ожидание) и fulfilled (выполнено). Когда промис находится в состоянии ожидания, он ожидает выполнения операции, которую он представляет. Когда операция завершается успешно, промис переходит в состояние выполнено и возвращает результат выполнения операции. Если операция не выполнена успешно, промис переходит в состояние rejected (отклонено) и возвращает ошибку.
Пример использования промиса:
let promise = new Promise(function(resolve, reject) < // Асинхронная операция, например, загрузка данных setTimeout(function() < resolve("Данные успешно загружены"); >, 3000); >); promise.then(function(result) < console.log(result); >).catch(function(error) < console.error(error); >);
В этом примере мы создаем новый промис, который представляет собой асинхронную операцию загрузки данных. Когда данные успешно загружены, промис возвращает строку “Данные успешно загружены”. Мы вызываем метод then на промисе, чтобы обработать результат выполнения операции и выводим его в консоль. Если операция не выполнена успешно, мы используем метод catch для обработки ошибки.
Промисы также позволяют использовать цепочку вызовов для выполнения нескольких операций последовательно:
let promise = loadData() .then(function(data) < return processData(data); >) .then(function(result) < return saveData(result); >) .catch(function(error) < console.error(error); >);
В этом примере мы загружаем данные с помощью функции loadData, затем обрабатываем их с помощью функции processData, сохраняем результат с помощью функции saveData, и обрабатываем любые ошибки, которые могут возникнуть в процессе выполнения операций.
Async/await
Async/await – это функциональность в JavaScript, которая позволяет писать асинхронный код с помощью промисов, упрощая его чтение и написание. По сути, является синтаксическим сахаром над промисами и позволяет писать асинхронный код в стиле синхронного.
В традиционном асинхронном программировании на JavaScript разработчики часто используют обратные вызовы (callbacks). Однако, это может привести к созданию громоздкого и трудночитаемого кода, который может быть труден для отладки и сопровождения.
Async/await, с другой стороны, предоставляет более лаконичный и выразительный способ написания асинхронного кода в JavaScript. Он позволяет писать код, который выглядит как синхронный, но при этом продолжает выполняться асинхронно.
Async/await использует ключевые слова async и await для обозначения асинхронных операций. Ключевое слово async указывается перед функцией, которая содержит асинхронный код, а ключевое слово await используется внутри этой функции для приостановки ее выполнения до тех пор, пока не будет выполнен промис. Когда код встречает ключевое слово await, выполнение функции приостанавливается до тех пор, пока промис, переданный в await, не завершится, и его результат не будет возвращен. Если промис успешно завершается, результат возвращается, и выполнение функции продолжается.
Обработка ошибок в асинхронном коде
Обработка ошибок в асинхронном коде в JavaScript может быть сложной, поскольку ошибки могут возникать как в асинхронных функциях, так и в функциях, которые эти асинхронные функции вызывают.
Для обработки ошибок в асинхронном коде в JavaScript часто используется конструкция try/catch, которая позволяет ловить ошибки в синхронном коде. Однако, когда речь идет об асинхронных операциях, эта конструкция может не сработать.
Вместо этого, для обработки ошибок в асинхронном коде в JavaScript используется конструкция try/catch/finally в сочетании с промисами (promises). В этом случае мы вызываем асинхронную функцию с помощью промиса, а затем используем метод catch для ловли ошибок:
async function getData() < try < const response = await fetch('https://example.com/data'); const data = await response.json(); return data; >catch (error) < console.error('Ошибка при получении данных:', error); >finally < console.log('Операция получения данных завершена'); >>
В этом примере мы используем try/catch для ловли ошибок при получении данных с помощью метода fetch. Если произошла ошибка, мы выводим ее в консоль с помощью console.error. Затем мы используем блок finally, который будет выполнен независимо от того, произошла ошибка или нет.
Если в асинхронной функции возникла ошибка, промис перейдет в состояние rejected, и управление передастся в блок catch. Если ошибки не произошло, промис перейдет в состояние fulfilled, и управление вернется в функцию getData.
Важно помнить, что try/catch не может ловить ошибки, которые возникают в другой части программы, например, в другой функции. В этом случае нужно использовать конструкцию try/catch в сочетании с промисами внутри каждой асинхронной функции, которая может вызвать ошибку.
ECMAScript 2016-2021
ECMAScript 2016, также известный как ES6, был выпущен в июне 2015 года и внес множество новых функций в язык. Некоторые из ключевых функций, включенных в ES6, включают:
- Let и Const для объявления переменных
- Стрелочные функции для более короткой записи функций
- Расширенный синтаксис объектов, включая сокращенную запись методов и свойств
- Шаблонные строки для удобной работы со строками
- Деструктуризация для извлечения значений из объектов и массивов
- Spread и Rest операторы для удобной работы с массивами и объектами
- Классы для создания объектов с помощью прототипного наследования
- Модульная система для организации кода в более мелкие и управляемые блоки
- Промисы для обработки асинхронного кода
- Генераторы для создания итераторов.
Кроме новых возможностей, введенных в ES6, последующие версии стандарта также добавляли новые функции и усовершенствования.
ECMAScript 2017 (ES2017)
Был выпущен в июне 2017 года и включал небольшое количество новых функций, среди которых:
- async/await: новый синтаксис для работы с асинхронным кодом, который упрощает написание и понимание асинхронного кода. Он позволяет использовать ключевые слова async и await для написания асинхронных функций и ожидания выполнения асинхронных операций.
- Object.values()/Object.entries(): новые методы объекта, которые позволяют получить массив значений или массив пар ключ-значение объекта. Это удобно для итерации по значениям объекта или для преобразования объекта в массив.
- String.prototype.padStart()/String.prototype.padEnd(): новые методы строки, которые позволяют добавлять символы в начало или конец строки, чтобы ее длина стала равной заданной. Это удобно для форматирования вывода или для выравнивания текста.
- Shared Memory and Atomics: новые API для работы с разделяемой памятью между потоками, которые позволяют эффективно обмениваться данными между несколькими потоками выполнения JavaScript. Это удобно для работы с многопоточными приложениями, такими как игры или приложения для обработки мультимедиа.
- trailing commas in function parameter lists and calls: новое правило, которое позволяет добавлять запятую в конец списка параметров функции или вызова функции без ошибки. Это удобно при работе с большими списками параметров, так как позволяет избежать ошибок при добавлении или удалении параметров.
ES2018
- Spread и Rest операторы для объектов: Оператор расширения (spread) позволяет развернуть объект и передать его свойства в другой объект или массив. Оператор сбора (rest) позволяет собрать несколько свойств объекта в один новый объект или массив.
- Методы объекта Object.entries() и Object.values(): Добавлены методы, которые возвращают массивы ключей и значений объекта соответственно. Это удобно для итерации по объектам и работе с ними.
- Функции padStart() и padEnd() для работы со строками: Добавлены функции, которые позволяют дополнять строки пробелами или другими символами в начале или конце строки до заданной длины. Это удобно для форматирования строк и выравнивания текста.
- Асинхронные итераторы: Добавлена возможность использовать итераторы для асинхронного кода. Это позволяет работать с асинхронными данными, такими как потоки, в цикле for await…of.
- RegExp named capture groups: Добавлена поддержка именованных групп в регулярных выражениях. Это упрощает извлечение данных из текста с помощью регулярных выражений.
- Оператор rest для параметров функции: Добавлена поддержка оператора rest для параметров функции, который позволяет передавать переменное количество аргументов в функцию в виде массива.
ES2019
- Метод Array.prototype.flat(): Добавлен метод flat(), который позволяет “сгладить” многомерный массив в одномерный массив путем удаления вложенных массивов.
- Методы Array.prototype.flatMap() и String.prototype.trimStart()/String.prototype.trimEnd(): Добавлены методы, которые упрощают работу со строками и массивами. Метод flatMap() комбинирует методы flat() и map(), позволяя сразу применять функцию к элементам массива и сгладить его. Методы trimStart() и trimEnd() удаляют пробелы с начала и конца строки.
- Деструктуризация объектов с использованием rest-оператора: Ранее, при использовании деструктуризации объектов, нельзя было извлечь оставшиеся свойства в новый объект. В ES2019 добавлен rest-оператор для деструктуризации объектов, который позволяет извлечь оставшиеся свойства и поместить их в новый объект.
- Операторы try/catch без аргументов: В ES2019 стало возможным использовать операторы try/catch без указания аргументов. Это позволяет легко и просто обрабатывать ошибки в коде.
- Метод Object.fromEntries(): Добавлен метод, который позволяет преобразовать массив пар ключ-значение в объект. Это удобно для преобразования данных из одного формата в другой.
ES2020
- Методы объектов, которые позволяют преобразовывать ключи в строковый формат: Добавлены методы Object.fromEntries(), Object.getOwnPropertyDescriptors() и Object.getPrototypeOf(), которые позволяют работать с ключами объектов как со строками.
- Optional chaining: Добавлена возможность использования оператора ?. для обращения к свойствам объекта, даже если они не определены. Это упрощает проверку наличия свойств вложенных объектов.
- Nullish coalescing: Добавлена возможность использовать оператор ?? для проверки наличия значения переменной, и если ее значение равно null или undefined, заменить его на заданное значение. Это упрощает работу с значениями по умолчанию.
- Метод String.prototype.matchAll(): Добавлен метод, который позволяет выполнить глобальный поиск всех совпадений регулярного выражения в строке. Это удобно для поиска всех вхождений итерируемого объекта.
- BigInt: Добавлен новый тип данных BigInt, который позволяет работать с целыми числами произвольной длины. Это решает проблему ограничений на размер чисел в JavaScript.
- Методы Promise.allSettled() и globalThis: Добавлен метод Promise.allSettled(), который возвращает массив объектов с результатами всех промисов, независимо от того, успешно они завершились или нет. Также был добавлен глобальный объект globalThis, который предоставляет доступ к глобальному объекту независимо от среды выполнения (например, в браузере или на сервере).
ES2021
- Методы массивов .flatMap() и .join(): Добавлены новые опции для метода .flatMap() для облегчения обработки данных и новая опция разделителя для метода .join().
- Операторы логического присваивания (||= и &&=): Добавлены операторы логического присваивания, которые позволяют упростить код, который присваивает значение переменной только в том случае, если она не определена.
- Promise.any(): Добавлен новый метод Promise.any(), который принимает массив промисов и возвращает результат первого завершенного промиса.
- WeakRefs: Добавлена возможность создания слабых ссылок на объекты, которые не будут участвовать в работе сборщика мусора.
- Числовые разделители: Добавлены разделители для чисел, которые позволяют упростить чтение больших чисел в коде.
- String.prototype.replaceAll(): Добавлен метод, который позволяет заменять все вхождения подстроки в строке.
TypeScript
Базовые понятия и принципы
TypeScript – это язык программирования, который расширяет функциональность JavaScript путем добавления статической типизации. Это позволяет облегчить разработку приложений, улучшить качество кода и снизить количество ошибок, связанных с типами данных.
Основные понятия TypeScript:
- Типы данных: TypeScript поддерживает несколько типов данных, таких как boolean, number, string, object, array и т.д. Это позволяет определить тип переменной, константы, параметра функции или возвращаемого значения функции.
- Интерфейсы: Интерфейсы в TypeScript определяют форму объекта. Они могут содержать свойства, методы и типы данных, которые объект должен реализовать.
- Классы: Классы в TypeScript позволяют определять объекты с конкретными свойствами и методами. Они могут иметь конструкторы, статические методы, методы доступа и многое другое.
- Функции: TypeScript поддерживает типизацию параметров функций и возвращаемых значений. Это позволяет облегчить чтение и понимание кода, а также улучшить качество и безопасность кода.
- Модули: Модули в TypeScript позволяют группировать связанный код в отдельные файлы для удобства работы. Они могут содержать классы, функции, интерфейсы и т.д.
- Преобразование типов: TypeScript предоставляет возможность преобразования типов данных, что позволяет управлять потерей данных во время преобразования.
Классы в TS
Классы в TypeScript представляют собой способ определения объектов с конкретными свойствами и методами. Они используются для создания объектов, которые могут иметь состояние и поведение, что делает их удобными для использования в различных приложениях.
Основные концепции, связанные с классами в TypeScript:
- Конструкторы в TypeScript определяются с помощью ключевого слова “constructor” и используются для инициализации свойств объекта при его создании. Они могут принимать параметры, которые передаются при создании объекта.
- Свойства класса определяют состояние объекта. Они могут иметь различные типы данных, такие как number, string, boolean и т.д. Они определяются внутри класса, но вне методов.
- Методы класса определяют поведение объекта. Они могут быть публичными, приватными или защищенными и могут иметь параметры и возвращаемые значения.
- Классы в TypeScript могут наследовать свойства и методы от других классов. Для этого используется ключевое слово “extends”. Наследующий класс может переопределить методы и свойства родительского класса и добавить свои собственные.
- Абстрактные классы в TypeScript представляют собой шаблоны для других классов и не могут быть использованы для создания объектов напрямую. Они используются для определения общих свойств и методов, которые должны быть реализованы в наследующих классах.
- Статические свойства и методы: Статические свойства и методы класса являются общими для всех объектов класса. Они могут быть использованы без создания объекта класса и используются для хранения общих данных и функциональности.
Интерфейсы
Интерфейсы в TypeScript представляют собой сущности, которые описывают форму или формат объекта. Они не являются частью сгенерированного кода JavaScript и не влияют на исполнение программы во время выполнения, но обеспечивают проверку типов во время компиляции.
Интерфейсы в TypeScript определяются с помощью ключевого слова “interface” и содержат список свойств и их типов. Например, простой интерфейс для объекта пользователя может выглядеть так:
interface User
Затем можно использовать этот интерфейс для определения типов объектов, которые соответствуют формату пользовательских данных. Например:
const user: User = < name: 'John Smith', age: 30, email: 'john@example.com' >;
Также интерфейсы могут использоваться в качестве типов для функций. Например, вот интерфейс для функции, которая принимает два числа и возвращает число:
interface MathFunction
Затем можно использовать этот интерфейс для определения типа функции, которая соответствует этой сигнатуре:
const add: MathFunction = (x, y) => x + y;
Интерфейсы также могут расширяться другими интерфейсами, что позволяет создавать более сложные интерфейсы, содержащие несколько свойств и методов.
Использование интерфейсов в TypeScript помогает улучшить структуру кода, сделать его более понятным и легким для поддержки. Проверка типов во время компиляции помогает предотвратить ошибки в работе программы, связанные с неправильным использованием объектов и функций.
Наследование в TS
Наследование – это механизм, позволяющий классу наследовать свойства и методы другого класса. В TypeScript наследование реализуется с помощью ключевого слова extends.
Рассмотрим пример базового класса Animal:
class Animal < name: string; constructor(name: string) < this.name = name; >move(distanceInMeters: number = 0) < console.log(`$moved $m.`); > >
Затем создадим производный класс Dog, который наследует свойства и методы класса Animal:
class Dog extends Animal < bark() < console.log('Woof! Woof!'); >>
В этом примере класс Dog наследует свойства и методы класса Animal. Кроме того, он определяет новый метод bark(), который не находится в базовом классе Animal.
Теперь можно создать экземпляр класса Dog и использовать как его собственные методы, так и методы, унаследованные от базового класса Animal:
const dog = new Dog('Buddy'); dog.bark(); // "Woof! Woof!" dog.move(10); // "Buddy moved 10m."
В этом примере метод bark() является уникальным для класса Dog, но метод move() унаследован от базового класса Animal.
Наследование в TypeScript может быть множественным, то есть класс может наследовать свойства и методы сразу от нескольких базовых классов. Однако TypeScript не поддерживает множественное наследование классов с помощью ключевого слова extends. Вместо этого можно использовать комбинацию наследования классов и интерфейсов.
В целом, наследование является одним из ключевых понятий ООП, которое позволяет создавать более гибкий и переиспользуемый код.
Type guards
TypeScript type guards (гварды типов) – это способ сужения типа переменной или выражения внутри условного блока кода. Они используются в TypeScript для улучшения проверки типов во время компиляции и облегчения разработки.
Например, если у вас есть переменная value с типом unknown, вы можете использовать следующий код, чтобы проверить ее тип и выполнить соответствующие действия:
function doSomething(value: unknown) < if (typeof value === 'string') < // value теперь имеет тип string console.log(value.toUpperCase()); >else if (Array.isArray(value)) < // value теперь имеет тип массива console.log(value.length); >else < // value все еще имеет тип unknown console.log('Unsupported type'); >>
В этом примере typeof и Array.isArray() являются гвардами типов, которые проверяют тип переменной value и позволяют выполнить соответствующие действия внутри условного блока кода.
Mixins
Mixin – это концепция в объектно-ориентированном программировании, которая позволяет объединять функциональность нескольких классов в один класс. В TypeScript mixins реализуются с помощью комбинации классов и миксиновых функций.
Миксин – это функция, которая принимает класс в качестве аргумента и возвращает новый класс с добавленной функциональностью. Для создания миксина в TypeScript можно использовать ключевое слово mixin. Например:
type Constructor = new (. args: any[]) => T; function Timestamped(Base: T) < return class extends Base < timestamp = Date.now(); >; > class MyClass extends Timestamped(Object) <> const myInstance = new MyClass(); console.log(myInstance.timestamp); // Выведет текущее время
В этом примере Timestamped – это миксин, который добавляет свойство timestamp с текущим временем к любому классу, который передается ему в качестве аргумента. Класс MyClass наследует функциональность Timestamped, и при создании экземпляра myInstance он получает свойство timestamp.
Миксины позволяют повторно использовать функциональность в различных классах, избежать дублирования кода и улучшить модульность и переиспользуемость вашего кода. Однако следует помнить, что множественное наследование может привести к сложностям и проблемам в проектировании классов, поэтому не следует злоупотреблять этой концепцией.