Как и любая компьютерная программа, JavaScript нуждается в наведении порядка в данных, в их структурировании. На языке JavaScript мы говорим, что если данные имеют одинаковую структуру, то они имеют одинаковый тип. Внутренняя организация данных может быть простой, как ДА или НЕТ, а может быть весьма замысловатой, как дерево HTML-элементов или маршруты на карте навигатора.

Примитивные типы. JavaScript предлагает разработчику несколько простых, примитивных, типов. Среди них: booleannumberstring. Примитивными эти типы называют за то, что значения этих типов нельзя поменять. Их можно клонировать, встроить в другие значения... Через минуту увидим как это происходит

Работа с наборами. Дополнительно JavaScript предлагает несколько типов объектов для работы с наборами — массивы, словари, множества. Это очень кстати, попробуй вспомни, как эффективно реализовать сортировку.

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

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

Примитивные типы

JavaScript различает семь типов:

undefined — обозначает тип значения переменной, которую объявили, но не инициализировали. Этот тип для данных, которых нет.

boolean — принимает только два значения «истина» и «ложь».

number — попытка научиться записывать любое число в ограниченную память компьютера (провалилась).

string — последовательность символов. JavaScript умеет преобразовывать значение любого примитивного типа в строку. Значение типа string в JavaScript неизменяемое, нельзя изменить одну букву в слове.

symbol — специальный тип данных. Задача значений типа symbol — служить специальными именами для обозначения специальных свойств объектов. Так много слов «специально», что мы дадим разъяснения ниже.

bigint — низкий поклон в сторону крипро- валют, токенов, контрактов. Они оперируют большими числами, и там этот тип данных очень пригождается. Скорее всего, не пригодится в вашем обычном фронтенде.

object — кроме примитивных типов в JavaScript есть структурные типы (объекты), а среди объектов особо выделяют функции. Обратите внимание, что в JavaScript функции это тоже объекты.

А как же nullnull — ещё одна возможность обозначить данные, которых нет. Да, в JavaScript есть целых два способа для обозначения ситуации, когда в переменной нет значения. Обратите внимание, что в JavaScript null это значение, а не тип данных и это значение может содержаться в JSON, а undefined в JSON не бывает.

Тип undefined

Когда вы объявляете переменную, но не присваиваете ей значение, переменная получает значение «по умолчанию» и это значение undefined, которое имеет тип undefined.

let buffer;

Переменная buffer объявлена с ключевым словом let, но до тех пока она не получила явного значения ее значение undefined.

Хитрость JavaScript в том, что иногда разработчик присваивает переменной результат работы функции, но переменная все равно остается undefined. В следующем примере buffer получает значение при объявлении. Можете ли вы догадаться почему buffer всё равно undefined?

function trickyComputation (){
  let a = 0;
  while(a<3){
    a+=2;
  }
}
let buffer = trickyComputation();

Все дело в том, что trickyComputationне возвращает значения. Попробуйте рассуждать о том, что цель функции — «присвоить значение» ключевому слову return. В вышеприведенном примере нет return, и этот самый воображаемый return остается неинициализированным, отсюда и undefined.

Будьте внимательны к функциям.

Тип boolean

В JavaScript про любое значение можно задать вопрос «похоже ли оно на правду?». Значение true — правда, false — ложь. Пустое значение обычно значит false.

Посмотрим на практике. Для проверки правдивости будем пользоваться вот таким методом showTrueness.

const showTrueness = (n,value)=> console.log(`${n} - ${value?'true':'false'}`);

Вот явная ложь:

// эти значения похожи на ложь
showTrueness(1,false); // false
showTrueness(2,''); // false
showTrueness(3,0) // false

Когда значение отличается от банально-начального, оно обычно истинно.

showTrueness(1, true); // true
showTrueness(2, 42); // true
showTrueness(3, 'false') // true

Как видите, слово false получает приговор — истина! Но это еще не самый курьёзный случай с boolean:

showTrueness(1, []); // true
showTrueness(2, {}); // true
showTrueness(3, [] === []); // false
showTrueness(4, {} === {}); // false

Хотя обычно пустые значения это ложь, пустой массив (1) и пустой объект (2) оказались истиной. А ещё в следующем отрывке JavaScript рассматривает число 0 эквивалентной строке ‘0’.

Видите 0=='0' показывает true. При этом по отдельности число 0 и строка '0' относятся к разным типам.

showTrueness(1, 0 == '0'); // true
showTrueness(2, 0); // false
showTrueness(3, '0'); // true

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

Для избежания сюрпризов используйте явное преобразования значений между типами и строгое сравнение (===) (! ==). В этом вам поможет набор правил eslinter от академии.

Тип number

Тип значения number предназначен для моделирования действительных чисел. Действительных чисел очень много — бесконечно много — а компьютерная память ограничена. Инженерам из IEEE пришлось даже выпустить отдельный международный стандарт для чисел с плавающей точкой — IEEE 754.

Во многих случаях числа с плавающей точкой ведут себя нормально. Сумма чисел 1 и 2 равна 3, а сумма чисел 0.1 и 0.2 равна сумме чисел 0.2 и 0.1. Но это не всегда так, и 0.1 + 0.2 может быть не равно 0.3. Если в школе вам говорили, что от перемены мест сумма не меняется, то в JavaScript это не всегда верно.

И если будете делать выписку в крипто-банке, два раза подумайте, в каком порядке вы хотите складывать числа. Потому что значение переменной currentBalanceV1 больше значения переменной currentBalanceV2, а всё потому что величины складываемых чисел сильно отличаются.

showTrueness(1, 1+2 === 3); // true

const lastMonthCredits = Array.from ({length:30},()=>Number.EPSILON/10);
const previousMonthBalance = 0.8;
const currentBalanceV1 = previousMonthBalance + lastMonthCredits.reduce((a,b)=>a+b,0);
const currentBalanceV2 = 0 + lastMonthCredits.reduce((a,b)=>a+b,previousMonthBalance);
showTrueness(2, currentBalanceV1>currentBalanceV2); // true

showTrueness(3, 0.1+0.2 === 0.2+0.1) // true

Советы при работе с числами

  • При работе с числами старайтесь сначала делать действия над числами сравнимой величины
  • При работе с числами старайтесь сравнивать их порядок, (что больше, а что меньше), а не равенство.
  • Изучите назначение предопределенных констант Number.MAX_SAFE_INTEGERNumber.EPSILON и других.

Задание для самопроверки. На сколько отличаются currentBalanceV1currentBalanceV2 и previousMonthBalance?

Тип string

Строки в JavaScript — это неизменяемые цепочки букв. Вы можете добавлять строки одну к другой, брать нужную букву по порядку. Нумерация букв в строке начинается с нуля, поэтому вы видите в примере (-1).

const show = (value)=>console.log(value);
const alfavit = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';
show(alfavit); // "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
show(alfavit[1-1]); // "а"
show(alfavit[33-1]); //"я"

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

alfavit[2-1]='*';
show(alfavit); // "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"

В виде строк можно хранить любой вид данных, нужно только договориться. Однако мы не рекомендуем вам изобретать велосипед. Лучше используйте общепринятые способы преобразования данных в строку и обратно: JSON.stringifyJSON.parse (для превращения в JSON и обратно), Intl.NumberFormatIntl.DateFormat.

Оператор typeof

Вы можете использовать оператор typeof для определения типа значения. Этот оператор воздействует на переменную и возвращает имя типа (если знает)

const show = (value, type)=>console.log(`typeof (${value}) is ${type}`);

const whatIsMyTypeName = (value)=>{
  switch(typeof value){
  case 'boolean':
  case 'number':
  case 'string': return show(value, typeof value);
  default: show(value, 'выясним позднее')
  }
}

whatIsMyTypeName(true); // typeof (true) is boolean"
whatIsMyTypeName(42); // "typeof (42) is number"
whatIsMyTypeName('миру-мир!'); // "typeof (миру-мир!) is string"
whatIsMyTypeName({}); // "typeof ([object Object]) is выясним позднее"
whatIsMyTypeName(window); // "typeof ([object Window]) is выясним позднее"

Встроенные типы

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

Array (массивы)

Сразу обратите внимание, что слово Array мы употребляем с большой буквы, в то время как booleannumberstring — с маленькой.

Если вы разработчик на JavaScript, то методы работы с массивами нужно знать и уметь вспомнить, даже если вас разбудили посреди ночи. Проверьте, что вы знаете о существовании методов массива length, from, map, sort, reduce, filter, find, indexOf, findIndex

Вы можете получить значение из массива по индексу, а можете заменить это значение другим. Индексом значения в массиве выступает число. Индексы идут от 0 в сторону увеличения.

JavaScript не будет следить за тем, обращаетесь ли вы по индексу правомерно или выходите за пределы массива. Вы даже можете положить новое значение по индексу «минус один» и получить его обратно. Однако вас ждут сюрпризы, если вы не будете контролировать размеры массива и значения индекса.

const show = (index, value)=> console.log(`${index}: ${value}`);
const items = [
'Chrome',
'Opera',
'Edge'
];

// Предоставляет элемент
show(1,items[1]) // "1: Opera"
// Ошибки нет, но и элемента тоже
show(100, items[100]); "100: undefined"

// Как, впрочем, и здесь
show(-1, items[-1]);// "-1: undefined"

items[12] = 'safari';
items[-1] = 'IE';

// Ошибки нет, а элемент появился
show(12, items[12]);//"12: safari"
show(-1, items[-1]);// "-1: IE"

// а где IE?
show('all', items)"all: Chrome,Opera,Edge,,,,,,,,,,safari"

// А тут всё ещё есть
show(-1, items[-1]);// "-1: IE"

Совет: контролируйте индексы, знайте размер массивов, с которыми работаете.

Set

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

const items = new Set(Array.from ({length:12},(_,ix)=>ix));

const str = (value)=>`${value}`;
const compare = (left,right)=>left<right?-1:right<left?1:0;
const byNumbericalValue = (left,right)=>compare(left,right);
const byStringValue =(left,right)=>compare(str(left),str(right));

console.log([...items].sort(byNumbericalValue));
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

console.log([...items].sort(byStringValue));
// [0, 1, 10, 11, 2, 3, 4, 5, 6, 7, 8, 9]

items.add(100);
items.add(20);

//порядок, в котором JavaScript отдает содержимое Set, зависит от порядка добавления элементов
console.log([...items].sort(byNumbericalValue));
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 100]

console.log([...items].sort(byStringValue));
// [0, 1, 10, 100, 11, 2, 20, 3, 4, 5, 6, 7, 8, 9]

console.log([...items]);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 100, 20]

Совет: для вывода элементов из Set в нужном порядке позаботьтесь о функции сортировки.

Кастомные объекты {} — ассоциативный массив

JavaScript объекты — это словари, где строковому ключу поставлен в соответствие элемент-значение. Возможны два синтаксиса доступа к значению ключа

const dictionary = {
  'language':'javascript',
  'type': 'structured',
  'age': 25
}

console.log(dictionary.language); // javascript
console.log(dictionary['language']); // javascript
const key = 'language';
console.log(dictionary[key]); // javascript

Важно! Ключ в кастомных объектах — это строковое значение. JavaScript неявно преобразует значение к строковому типу перед использованием его в качестве ключа.

Это ограничение не позволяет использовать объекты для создания связей между HTML-элементами и дополнительными данным, нужными для работы программы. Для этого используйте тип Map.

Map

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

const DictionaryCO = {}; // инициализируем вариант ассоциативного массива на основе обычного объекта
const DictionaryMap = new Map(); // инициализируем вариант ассоциативного массива на основе встроенного типа Map

// Первый эксперимент будет связывать ключ 'k1' и значение 'v1'
const aKey = 'k1';
const aValue = 'v1';

// Второй эксперимент будет связывать ключ в виде объекта и значение - строку
// такой вариант в жизни встречается если для нескольких HTML элементов нужно ввести дополнительные данные, которые нельзя положить в data-xxx атрибут
const bKey = {x:1};
const bValue = 'v2';
// Третий эксперимент покажет нам недостатки ассоциативного массива на основе обычного объекта. Мы используем в качестве ключа - другой объект
const cKey = {y:2};
const cValue = 'v3';

// заполняем первый ассоциативный массив (который в виде объекта)
DictionaryCO[aKey] = aValue;
DictionaryCO[bKey] = bValue;
DictionaryCO[cKey] = cValue;

// заполняем второй ассоциативный массива (которы в виде Map)
DictionaryMap.set(aKey, aValue);
DictionaryMap.set(bKey, bValue);
DictionaryMap.set(cKey, cValue);

console.log('a object', DictionaryCO[aKey]); // "a object", "v1"
console.log('b object', DictionaryCO[bKey]); // "b object", "v2"
// хотя мы пытаемся получить значение по ключу cKey, почему-то
// мы получаем значение, которое связали с ключом bKey
console.log('c object, oops!', DictionaryCO[cKey]); // "c object, oops!", "v2"

console.log('a map', DictionaryMap.get(aKey)); // "a map", "v1"
console.log('b map', DictionaryMap.get(bKey)); // "b map", "v2"
// в этом варианте ассоциативного массива все сработало так, как мы и ожидали
console.log('c map', DictionaryMap.get(cKey)); // "c map", "v3"

Собственные структуры данных

Утиная типизация

Программисты работают с лозунгом «Algorithms + Data Structures = Programs». При работе с JavaScript разработчик преобразует требования заказчика в алгоритмы и структуры данных. Примитивных типов данных, доступных в JavaScript явно недостаточно для всего разнообразия бизнес задач, поэтому приходится использовать кастомные структуры. Для приложения электронной очереди потребуется структура с номером и временем, для умного дома — структура с данными о температуре чайника, заполненности холодильника и т. п.

let cusomerInQueue = {
  numberOnScreen: 'A42',
  timeTaken: '11:40'
}

let smartHouseState = {
  kettler: 80,
  fridge:{
    milk: true,
    banana: false,
  },
}

В вашей программе вы получаете сведения о заполнении холодильника, потому что ожидаете наличия поля fridge в структуре объекта, а не потому, что переменная называется smartHouseState. Выяснив, что в структуре объекта нет поля fridge вы смело можете сказать, что значение переменной cusomerInQueueне связано с управлением умным домом, и наоборот наличие такого поля в переменной smartHouseState подсказывает вам, что её значение описывает умный дом. Такой подход, когда о назначении значения вы судите по его структуре, называется структурной типизацией.

Иногда вы знаете структуру значения (потому что знаете), а иногда вам приходится как-то догадываться. Но как, ведь typeof в этой ситуации не поможет?

Object.keys и другие шпионские средства

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

Основными помощниками в исследовании полученных данных вам будут

  • Статический метод Object.keys
  • Оператор typeof
  • Оператор instanceof
  • Статический метод Object.hasOwn
const someValue = {
  title: 'Cruella',
  release: new Date(2021,05,03),
  empty: undefined,
}

console.log('all keys', Object.keys(someValue));
// "all keys", ["title", "release", "empty"]

console.log('typeof key release', typeof someValue.release);
// "typeof key release", "object"

console.log('instance of Date', someValue.release instanceof Date)
// "instance of Date", true

console.log('missing value', typeof someValue.empty);
// "missing value", "undefined"

console.log('missing key', Object.hasOwn(someValue, 'missing'));
// "missing key", false

Проблемы типов JavaScript

Динамическая структурная типизация JavaScript имеет давнюю историю и восходит к идее создания небольших обработчиков событий в статических HTML страницах. С тех пор все поменялось, и динамическая типизация становится серьезным риском при постепенном и постоянном совершенствовании приложения.

Например, у нас была функция transform. Мы обнаружили, что она ломается при получении значения null.

const transform = (value) => value.replace('с', 'б');

let data = 'соль'

console.log(transform(data).toUpperCase()) // "БОЛЬ"
data = null;
try {
  console.log(transform(data).toUpperCase()) // не выполнится
} catch (err) {
  console.log(err.message) // "Cannot read properties of null (reading 'replace')"
}

Исправили программу в одном месте — она начала ломаться в другом. Как разорвать замкнутый круг?

const transform2 = (value) => {
  if (typeof value === 'string') {
    return value.replace('с', 'б');
  }
}
data = 'соль';
console.log(transform2(data).toUpperCase()) // "БОЛЬ"
data = null
try {
  console.log(transform2(data).toUpperCase())
} catch (err) {
  console.log(err.message) // "Cannot read properties of undefined (reading 'toUpperCase')"
}

Вам поможет TypeScript! Но об этом в следующий раз.

Узнать больше


«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

ТелеграмПодкастБесплатные учебники

Читать дальше

Объект URL в JavaScript: полный разбор

Объект URL в JavaScript: полный разбор

Объект URL в JavaScript представляет URL-адрес и предоставляет удобные методы для работы с ним. Он позволяет анализировать, конструировать и декодировать URL-адреса.

Создать объект URL можно двумя способами:

Конструктор URL() — самый распространённый способ, в котором вы передаёте любой URL в виде строки в качестве аргумента.

const url = new URL("https://www.example.com/path?query=123#hash");

Использование window.location — это глобальный объект в браузерах, который содержит информацию о текущем URL.

const currentUrl = new URL(window.location.href);
Читать дальше
JS
  • 23 января 2024
Знакомство с JavaScript

Знакомство с JavaScript

Теперь, когда вы знаете, как создать структуру веб-страницы с помощью HTML и оформить ее стилями с помощью CSS, пришло время оживить её с помощью JavaScript (JS). JavaScript — это мощный язык программирования, который используется для создания интерактивных и динамических веб-сайтов.

Вы можете добавить JavaScript в ваш HTML-документ двумя способами:

Встроенный JavaScript: непосредственно в HTML-документ, в тегах <script>:

<script>
  alert("Привет, мир!");
</script>

Внешний JavaScript: подключение внешнего .js файла к HTML-документу:

<script src="script.js"></script>
Читать дальше
JS
  • 1 ноября 2023
Событие onclick в JS на примерах

Событие onclick в JS на примерах

Интерактивность — ключевой компонент любого современного сайта. И одним из наиболее часто используемых событий для создания интерактивности является событие onclick. В этой статье мы подробно разберёмся, что такое событие onclick, как его использовать и приведем примеры применения.

Событие onclick — это событие JavaScript, которое активируется, когда пользователь кликает на определенный элемент страницы. Это может быть кнопка, ссылка, изображение или любой другой элемент, на который можно нажать.

Читать дальше
JS
  • 30 октября 2023
Как перевернуть сайт. Самая короткая инструкция

Как перевернуть сайт. Самая короткая инструкция

Не представляем, зачем это может понадобиться, но не могли пройти мимо.

Никакой магии. Мы вызываем JavaScript-функцию rotateBody(), которая применяет свойство transform с значением rotate(180deg) к элементу <body>. Когда вы нажмете на кнопку «Перевернуть», всё, что находится внутри <body> будет повернуто на 180 градусов (то есть, встанет вниз головой)

function rotateBody() {
  document.body.style.transform = 'rotate(180deg)';
}

<button onclick="rotateBody()">Перевернуть</button>

Но такой код повернёт страницу только один раз. Если нужно, чтобы она возвращалась обратно при втором клике, усложним код:

let isRotated = false;

function rotateBody() {
  if (isRotated) {
    document.body.style.transform = 'rotate(0deg)';
    document.body.style.direction = "ltr";
  } else {
    document.body.style.transform = 'rotate(180deg)';
    document.body.style.direction = "rtl";
  }
  isRotated = !isRotated;
}

Надеемся, вы прочитали это описание до того, как нажать на кнопку.

JS
  • 25 октября 2023
Как узнать геолокацию: Geolocation API

Как узнать геолокацию: Geolocation API

Geolocation API позволяет сайтам запрашивать, а пользователям предоставлять свое местоположение веб-приложениям. Геолокация может использоваться для выбора города в интернет-магазине, отображения пользователя на карте или навигации в ближайший гипермаркет.

Основной метод Geolocation API — getCurrentPosition(), но есть и другие методы и свойства, которые могут пригодиться.

Читать дальше
JS
  • 16 октября 2023
Что такое localStorage и как им пользоваться

Что такое localStorage и как им пользоваться

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

До localStorage разработчики часто использовали cookies, но они были не очень удобны: мало места и постоянная передача данных туда-сюда. LocalStorage появился, чтобы сделать процесс более простым и эффективным.

Читать дальше
JS
  • 12 октября 2023
Случайное число из диапазона

Случайное число из диапазона

Допустим, вам зачем-то нужно целое случайное число от min до max. Вот сниппет, который поможет:

function getRandomInRange(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
  1. Math.random () генерирует случайное число между 0 и 1. Например, нам выпало число 0.54.
  2. (max — min + 1): определяет количество возможных значений в заданном диапазоне. 10 - 0 + 1 = 11. Это значит, что у нас есть 11 возможных значений (0, 1, 2, ... 10).
  3. Math.random () * (max — min + 1): умножает случайное число на количество возможных значений: 0.54 * 11 = 5.94.
  4. Math.floor (): округляет число вниз до ближайшего целого. Так, Math.floor(5.94) = 5.
  5. ... + min: смещает диапазон так, чтобы минимальное значение соответствовало min. Но в нашем примере, так как min = 0, это не изменит результат. Пример: 5 + 0 = 5.
  6. Итак, в нашем примере получилось случайное число 5 из диапазона от 0 до 10.

Чтобы протестировать, запустите:

console.log(getRandomInRange(1, 10)); // Тест
JS
  • 7 сентября 2023
В чём разница между var и let

В чём разница между var и let

Если вы недавно пишете на JavaScript, то наверняка задавались вопросом, чем отличаются var и let, и что выбрать в каждом случае. Объясняем.

var и let — это просто два способа объявить переменную. Вот так:

var x = 10;
let y = 20;

Переменная, объявленная через var, доступна только внутри «своей» функции, или глобально, если она была объявлена вне функции.

function myFunction() {
  var z = 30;
  console.log(z); // 30
}
myFunction();
console.log(z); // ReferenceError

Это может создавать неожиданные ситуации. Допустим, вы создаёте цикл в функции и хотите, чтобы переменная i осталась в этой функции. Если вы используете var, эта переменная «утечёт» за пределы цикла и будет доступна во всей функции.

Переменные, объявленные с помощью let доступны только в пределах блока кода, в котором они были объявлены.

if (true) {
  let a = 40;
  console.log(a); // 40
}
console.log(a); // ReferenceError

В JavaScript блок кода — это участок кода, заключённый в фигурные скобки {}. Это может быть цикл, код в условном операторе или что-нибудь ещё.

if (true) {
  let blockScoped = "Я виден только здесь";
  console.log(blockScoped); // "Я виден только здесь"
}

// здесь переменная blockScoped недоступна
console.log(blockScoped); // ReferenceError

Если переменная j объявлена в цикле с let, она останется только в этом цикле, и попытка обратиться к ней за его пределами вызовет ошибку.

Читать дальше
JS
  • 30 августа 2023