[Разработка веб-сайтов, JavaScript] Мои любимые трюки в JavaScript (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Приветствую. Представляю вашему вниманию перевод статьи «My Favorite JavaScript Tips and Tricks», опубликованной 28 июля 2020 года автором Tapas Adhikary
Большинство языков программирования являются достаточно открытыми, чтобы предоставить разработчикам возможность делать одно и то же разными способами. JavaScript не является исключением. Зачастую, у нас есть разные пути достижения одинакового результата, что порой даже может сбивать с толку.
При этом, некоторые приёмы обладают преимуществами перед аналогами. В данной статье я привожу список моих любимых. Уверен, какие-то из них вам также будут знакомы.
1. Забудьте о конкатенации, используйте шаблонные строки (литералы)
Конкатенация строк с помощью оператора "+" – это старая школа. Более того, конкатенация строк с участием большого количества переменных (или выражений) повышает риск возникновения путаницы и ошибок.
let name = 'Charlse';
let place = 'India';
let isPrime = bit => {
return (bit === 'P' ? 'Prime' : 'Nom-Prime');
}
// конкатенация строк с помощью оператора "+"
let messageConcat = 'Mr. ' + name + ' is from ' + place + '. He is a' + ' ' + isPrime('P') + ' member.'
Шаблонные строки (или литералы) позволяют встраивать выражения прямо в текст. Они обладают уникальным синтаксисом, при котором строка заключается в обратные кавычки (``). Шаблонная строка может содержать места для подстановки динамических значений. Такие места отмечаются знаком доллара и фигурными скобками. Например, ${выражение}.
Демонстрация их применения:
let name = 'Charlse';
let place = 'India';
let isPrime = bit => {
return (bit === 'P' ? 'Prime' : 'Nom-Prime');
}
// использование шаблонной строки
let messageTemplateStr = `Mr. ${name} is from ${place}. He is a ${isPrime('P')} member.`
console.log(messageTemplateStr);
2. isInteger
Это аккуратный способ узнать, является ли значение целым числом. Встроенный в JavaScript API "Number" предоставляет для этого метод "isInteger()". Очень полезная штука, о которой следует знать.
let mynum = 123;
let mynumStr = "123";
console.log(`${mynum} is a number?`, Number.isInteger(mynum));
console.log(`${mynumStr} is a number?`, Number.isInteger(mynumStr));
Результат:
3. Значение как число
Вы когда-нибудь обращали внимание, что "event.target.value" всегда возвращает строковое значение, даже если для поля ввода "input" задан тип "number"?
Посмотрите на пример ниже. У нас есть простое поле ввода, рассчитанное только на числа, а также обработчик, срабатывающий, когда отпускают нажатую ранее кнопку клавиатуры.
<input type='number' onkeyup="trackChange(event)" />
Обработчик извлекает значение поля с помощью "event.target.value". Но возвращаемое значение имеет строковый тип. Мы получим дополнительную головную боль из-за необходимости преобразовывать это значение в целое число. А что, если поле ввода допускало бы и дробные числа (типа 16.56)? Тогда для преобразования пришлось бы использовать "parseFloat()"? Сколько же лишней работы и риска что-то напутать!
function trackChange(event) {
let value = event.target.value;
console.log(`is ${value} a number?`, Number.isInteger(value));
}
Чтобы сразу получать числовое значение, используйте "event.target.valueAsNumber".
let valueAsNumber = event.target.valueAsNumber;
console.log(`is ${value} a number?`, Number.isInteger(valueAsNumber));
4. Сокращение с помощью AND
Давайте рассмотрим ситуацию, в которой у нас есть логическое значение и функция.
let isPrime = true;
const startWatching = () => {
console.log('Started Watching!');
}
Немало кода получается ради проверки логического условия и вызова функции.
if (isPrime) {
startWatching();
}
А как насчёт использования сокращённой записи вместе с оператором AND (&&)? Да, условный оператор "if" нам больше не нужен. Круто, правда?
isPrime && startWatching();
5. Значение по умолчанию с помощью OR
Если для переменной необходимо предусмотреть запасное значение по умолчанию, это достаточно просто реализуется с помощью оператора OR.
let person = {name: 'Jack'};
// если свойство "age" равно "undefined", устанавливает значение 35
let age = person.age || 35;
console.log(`Age of ${person.name} is ${age}`);
6. Произвольные значения
Генерирование произвольных значений или получение произвольного элемента массива – очень полезные методы, которые стоит держать под рукой. Я использую их почти в каждом своём проекте.
Получить произвольный элемент из массива
let planets = ['Mercury ', 'Mars', 'Venus', 'Earth', 'Neptune', 'Uranus', 'Saturn', 'Jupiter'];
let randomPlanet = planets[Math.floor(Math.random() * planets.length)];
console.log('Random Planet', randomPlanet);
Генерирование произвольного числа из диапазона с указанием минимального и максимального значений
let getRandom = (min, max) => {
return Math.round(Math.random() * (max - min) + min);
}
console.log('Get random', getRandom(0, 10));
Примечание от переводчика
Согласно справочнику learn.javascript.ru, такой способ является не очень верным, так как вероятность получения минимального и максимального значений в 2 раза меньше, чем любого другого числа. В справочнике приводится более корректный вариант решения.
7. Значения параметров функции по умолчанию
В JavaScript параметры функции подобны локальным переменным. При вызове этой самой функции вы можете и не передавать значения для её параметров. В этом случае они принимают значение "undefined", что может привести к нежелательным последствиям.
Существует простой способ передачи значения по умолчанию для параметров функции при их определении. В примере ниже для параметра "message" функции "greetings" передаётся значение по умолчанию "Hello".
let greetings = (name, message='Hello,') => {
return `${message} ${name}`;
}
console.log(greetings('Jack'));
console.log(greetings('Jack', 'Hola!'));
8. Обязательные параметры функции
В дополнение к технике указания значений по умолчанию для параметров функции, мы также можем делать параметры обязательными.
Сначала объявляем функцию, которая будет выдавать в консоль сообщение об ошибке
let isRequired = () => {
throw new Error('This is a mandatory parameter.');
}
Затем эту функцию присваиваем как значение по умолчанию для параметров, которые хотим сделать обязательными. Помните, что если при вызове функции её параметр равен "undefined", подставляется значение по умолчанию, в обратном случае значение по умолчанию игнорируется.
let greetings = (name=isRequired(), message='Hello,') => {
return `${message} ${name}`;
}
console.log(greetings());
В примере выше параметр "name" будет иметь значение "undefined", из-за чего будет произведена попытка установить значение по умолчанию, которым и выступает функция "isRequired()". Будет вызвана ошибка:
9. Оператор "Запятая"
Я столько раз использовал запятую в коде, но не осознавал, что она является отдельным оператором.
В JavaScript оператор запятой используется для оценки каждого из операндов слева направо и возврата значения последнего операнда.
let count = 1;
let ret = (count++, count);
console.log(ret);
В примере выше значением переменной "ret" будет число 2. По тому же принципу, результатом следующего кода будет вывод в консоль числа 32.
let val = (12, 32);
console.log(val);
Где мы его используем? Есть идеи? Чаще всего оператор запятой используется параметров в цикле "for".
В примере ниже оператор запятой задаёт значение переменной "j" после объявления счётчика "i".
for (var i = 0, j = 50; i <= 50; i++, j--)
10. Объединение нескольких объектов
У вас может возникнуть потребность объединить вместе два объекта, чтобы создать третий, более полный. В этом случае можно использовать оператор "..." (да, три точки).
Рассмотрим на примере:
let emp = {
'id': 'E_01',
'name': 'Jack',
'age': 32,
'addr': 'India'
};
let job = {
'title': 'Software Dev',
'location': 'Paris'
};
Их можно объединить с помощью spread-оператора (оператора расширения):
// spread-оператор
let merged = {...emp, ...job};
console.log('Spread merged', merged);
Существует и другой путь такого объединения. С помощью "Object.assign()":
console.log('Object assign', Object.assign({}, emp, job));
В результате получается:
Обратите внимание, что и spread-оператор и "Object.assign" выполняют поверхностное (shallow) объединение. При поверхностном объединении, если свойства повторяются, то происходит перезапись первого объекта данными из таких же свойств второго.
Для глубокого объединения объектов, следует использовать, например, библиотеку lodash
11. Деструктуризация
Деструктуризация – это техника, позволяющая извлекать значения элементов массива или свойств объекта в набор переменных. Давайте рассмотрим её на примерах
Массив
У нас есть массив со смайликами
Чтобы осуществить его деструктуризацию, следует использовать следующий синтаксис:\
let [fire, clock, , watermelon] = emojis;
Это то же самое, что и "let fire = emojis[0];", но способ гораздо более гибкий. Вы обратили внимание, что я пропустил присваивание смайлика "награда", просто оставив пустое пространство между запятыми? Итак, что у нас получится?
console.log(fire, clock, watermelon);
Результат:
Думаю, здесь также стоит упомянуть оператор "rest". Если вы хотите деструктурировать массив, присвоив один или более элементов переменным, а все оставшиеся элементы поместить в другой массив, это можно реализовать с помощью параметра "...rest", как показано ниже.
let [fruit, ...rest] = emojis;
console.log(rest);
Результат:
Объект
Подобно массивам, деструктурировать можно и объекты
let shape = {
name: 'rect',
sides: 4,
height: 300,
width: 500
};
Деструктурируя объект подобным образом, мы присваиваем переменным значения двух первых свойств объекта, а остальные помещаем в другой объект.
let {name, sides, ...restObj} = shape;
console.log(name, sides);
console.log(restObj);
Результат
Дополнительно почитать на тему деструктуризации можно здесь.
12. Обмен значений переменных
С помощью техники деструктуризации, с которой мы ознакомились выше, это сделать очень просто
let fire = '';
let fruit = '';
[fruit, fire] = [fire, fruit];
console.log(fire, fruit);
13. isArray
Ещё один полезный метод, на этот раз позволяющий определить, являются ли входящие данные массивом
let emojis = ['', '️', '', ''];
console.log(Array.isArray(emojis));
let obj = {};
console.log(Array.isArray(obj));
14."'undefined" против "null"
"undefined" – присутствует у переменной, которая была объявлена, но значение для неё задано не было
"null" – обозначает пустое или несуществующее значение, которое явно присваивается переменной
"undefined" и "null" не равны при строгом сравнивании
undefined === null // false
Дополнительно почитать на тему разницы между этими двумя значениями можно здесь
15. Получение фрагментов url-адреса
Объект "window.location" имеет набор полезных методов и свойств. С помощью них мы можем получить данные про протокол, хост, порт, домен и тому подобное из url-адреса браузера.
Одно из свойств, которое мне кажется очень полезным
window.location.search
Свойство "search" возвращает фрагмент строки url-адреса, находящийся после вопросительного знака: "?project=js".
Для получения параметров запроса, помимо "location.search" можно использовать ещё один полезный API, называемый "URLSearchParams".
let project = new URLSearchParams(location.search).get('project');
В результате получаем "js"
Подробнее об этой теме читайте здесь
Это не конец
Существует множество других полезных приёмов. Я решил добавлять их в репозиторий вместе с небольшими примерами по мере того, как буду с ними сталкиваться.
Какие у вас любимые приёмы JavaScript? Как насчёт поделиться ими в комментариях ниже?
===========
Источник:
habr.com
===========
===========
Автор оригинала: Tapas Adhikary
===========Похожие новости:
- [Разработка веб-сайтов, Разработка игр, Разработка мобильных приложений, Разработка под Linux, Разработка под Windows] Свободная веб-энциклопедия для любых IT-проектов на собственном движке
- [Облачные сервисы, Программирование, Разработка веб-сайтов] Serverless и полтора программиста
- [Разработка веб-сайтов, CSS, JavaScript, Клиентская оптимизация] Оптимизация производительности фронтенда. Часть 1. Critical Render Path
- [Разработка веб-сайтов, CSS, HTML] CSS Grid понятно для всех
- [Разработка веб-сайтов, JavaScript, Тестирование веб-сервисов] Утреннее шоу с Lucas F. Costa: JS, CS и тестирование веб-приложений
- [Python, JavaScript, Программирование] Разбор статьи из журнала «Код» (Яндекс Практикум)
- [JavaScript, Интерфейсы, ReactJS, TypeScript] Когда и CRA мало. Доклад Яндекса
- [CMS, JavaScript, Распределённые системы] Создание собственной Headless CMS и интеграция с блогом (перевод)
- [Ajax, PHP, MySQL, JavaScript, jQuery] Пишем комментарии для сайта на чистом PHP + MySQL + Ajax
- [Карьера в IT-индустрии] Работать по 12 часов в сутки и не спать по 3 дня кряду, или как я стал frontend-разработчиком
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_javascript, #_javascript, #_web_development, #_frontend, #_razrabotka_vebsajtov (разработка веб-сайтов), #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_javascript
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 26-Ноя 15:02
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Приветствую. Представляю вашему вниманию перевод статьи «My Favorite JavaScript Tips and Tricks», опубликованной 28 июля 2020 года автором Tapas Adhikary Большинство языков программирования являются достаточно открытыми, чтобы предоставить разработчикам возможность делать одно и то же разными способами. JavaScript не является исключением. Зачастую, у нас есть разные пути достижения одинакового результата, что порой даже может сбивать с толку. При этом, некоторые приёмы обладают преимуществами перед аналогами. В данной статье я привожу список моих любимых. Уверен, какие-то из них вам также будут знакомы. 1. Забудьте о конкатенации, используйте шаблонные строки (литералы) Конкатенация строк с помощью оператора "+" – это старая школа. Более того, конкатенация строк с участием большого количества переменных (или выражений) повышает риск возникновения путаницы и ошибок. let name = 'Charlse';
let place = 'India'; let isPrime = bit => { return (bit === 'P' ? 'Prime' : 'Nom-Prime'); } // конкатенация строк с помощью оператора "+" let messageConcat = 'Mr. ' + name + ' is from ' + place + '. He is a' + ' ' + isPrime('P') + ' member.' Шаблонные строки (или литералы) позволяют встраивать выражения прямо в текст. Они обладают уникальным синтаксисом, при котором строка заключается в обратные кавычки (``). Шаблонная строка может содержать места для подстановки динамических значений. Такие места отмечаются знаком доллара и фигурными скобками. Например, ${выражение}. Демонстрация их применения: let name = 'Charlse';
let place = 'India'; let isPrime = bit => { return (bit === 'P' ? 'Prime' : 'Nom-Prime'); } // использование шаблонной строки let messageTemplateStr = `Mr. ${name} is from ${place}. He is a ${isPrime('P')} member.` console.log(messageTemplateStr); 2. isInteger Это аккуратный способ узнать, является ли значение целым числом. Встроенный в JavaScript API "Number" предоставляет для этого метод "isInteger()". Очень полезная штука, о которой следует знать. let mynum = 123;
let mynumStr = "123"; console.log(`${mynum} is a number?`, Number.isInteger(mynum)); console.log(`${mynumStr} is a number?`, Number.isInteger(mynumStr)); Результат: 3. Значение как число Вы когда-нибудь обращали внимание, что "event.target.value" всегда возвращает строковое значение, даже если для поля ввода "input" задан тип "number"? Посмотрите на пример ниже. У нас есть простое поле ввода, рассчитанное только на числа, а также обработчик, срабатывающий, когда отпускают нажатую ранее кнопку клавиатуры. <input type='number' onkeyup="trackChange(event)" />
Обработчик извлекает значение поля с помощью "event.target.value". Но возвращаемое значение имеет строковый тип. Мы получим дополнительную головную боль из-за необходимости преобразовывать это значение в целое число. А что, если поле ввода допускало бы и дробные числа (типа 16.56)? Тогда для преобразования пришлось бы использовать "parseFloat()"? Сколько же лишней работы и риска что-то напутать! function trackChange(event) {
let value = event.target.value; console.log(`is ${value} a number?`, Number.isInteger(value)); } Чтобы сразу получать числовое значение, используйте "event.target.valueAsNumber". let valueAsNumber = event.target.valueAsNumber;
console.log(`is ${value} a number?`, Number.isInteger(valueAsNumber)); 4. Сокращение с помощью AND Давайте рассмотрим ситуацию, в которой у нас есть логическое значение и функция. let isPrime = true;
const startWatching = () => { console.log('Started Watching!'); } Немало кода получается ради проверки логического условия и вызова функции. if (isPrime) {
startWatching(); } А как насчёт использования сокращённой записи вместе с оператором AND (&&)? Да, условный оператор "if" нам больше не нужен. Круто, правда? isPrime && startWatching();
5. Значение по умолчанию с помощью OR Если для переменной необходимо предусмотреть запасное значение по умолчанию, это достаточно просто реализуется с помощью оператора OR. let person = {name: 'Jack'};
// если свойство "age" равно "undefined", устанавливает значение 35 let age = person.age || 35; console.log(`Age of ${person.name} is ${age}`); 6. Произвольные значения Генерирование произвольных значений или получение произвольного элемента массива – очень полезные методы, которые стоит держать под рукой. Я использую их почти в каждом своём проекте. Получить произвольный элемент из массива let planets = ['Mercury ', 'Mars', 'Venus', 'Earth', 'Neptune', 'Uranus', 'Saturn', 'Jupiter'];
let randomPlanet = planets[Math.floor(Math.random() * planets.length)]; console.log('Random Planet', randomPlanet); Генерирование произвольного числа из диапазона с указанием минимального и максимального значений let getRandom = (min, max) => {
return Math.round(Math.random() * (max - min) + min); } console.log('Get random', getRandom(0, 10)); Примечание от переводчика
Согласно справочнику learn.javascript.ru, такой способ является не очень верным, так как вероятность получения минимального и максимального значений в 2 раза меньше, чем любого другого числа. В справочнике приводится более корректный вариант решения. 7. Значения параметров функции по умолчанию В JavaScript параметры функции подобны локальным переменным. При вызове этой самой функции вы можете и не передавать значения для её параметров. В этом случае они принимают значение "undefined", что может привести к нежелательным последствиям. Существует простой способ передачи значения по умолчанию для параметров функции при их определении. В примере ниже для параметра "message" функции "greetings" передаётся значение по умолчанию "Hello". let greetings = (name, message='Hello,') => {
return `${message} ${name}`; } console.log(greetings('Jack')); console.log(greetings('Jack', 'Hola!')); 8. Обязательные параметры функции В дополнение к технике указания значений по умолчанию для параметров функции, мы также можем делать параметры обязательными. Сначала объявляем функцию, которая будет выдавать в консоль сообщение об ошибке let isRequired = () => {
throw new Error('This is a mandatory parameter.'); } Затем эту функцию присваиваем как значение по умолчанию для параметров, которые хотим сделать обязательными. Помните, что если при вызове функции её параметр равен "undefined", подставляется значение по умолчанию, в обратном случае значение по умолчанию игнорируется. let greetings = (name=isRequired(), message='Hello,') => {
return `${message} ${name}`; } console.log(greetings()); В примере выше параметр "name" будет иметь значение "undefined", из-за чего будет произведена попытка установить значение по умолчанию, которым и выступает функция "isRequired()". Будет вызвана ошибка: 9. Оператор "Запятая" Я столько раз использовал запятую в коде, но не осознавал, что она является отдельным оператором. В JavaScript оператор запятой используется для оценки каждого из операндов слева направо и возврата значения последнего операнда. let count = 1;
let ret = (count++, count); console.log(ret); В примере выше значением переменной "ret" будет число 2. По тому же принципу, результатом следующего кода будет вывод в консоль числа 32. let val = (12, 32);
console.log(val); Где мы его используем? Есть идеи? Чаще всего оператор запятой используется параметров в цикле "for". В примере ниже оператор запятой задаёт значение переменной "j" после объявления счётчика "i". for (var i = 0, j = 50; i <= 50; i++, j--)
10. Объединение нескольких объектов У вас может возникнуть потребность объединить вместе два объекта, чтобы создать третий, более полный. В этом случае можно использовать оператор "..." (да, три точки). Рассмотрим на примере: let emp = {
'id': 'E_01', 'name': 'Jack', 'age': 32, 'addr': 'India' }; let job = { 'title': 'Software Dev', 'location': 'Paris' }; Их можно объединить с помощью spread-оператора (оператора расширения): // spread-оператор
let merged = {...emp, ...job}; console.log('Spread merged', merged); Существует и другой путь такого объединения. С помощью "Object.assign()": console.log('Object assign', Object.assign({}, emp, job));
В результате получается: Обратите внимание, что и spread-оператор и "Object.assign" выполняют поверхностное (shallow) объединение. При поверхностном объединении, если свойства повторяются, то происходит перезапись первого объекта данными из таких же свойств второго. Для глубокого объединения объектов, следует использовать, например, библиотеку lodash 11. Деструктуризация Деструктуризация – это техника, позволяющая извлекать значения элементов массива или свойств объекта в набор переменных. Давайте рассмотрим её на примерах Массив У нас есть массив со смайликами Чтобы осуществить его деструктуризацию, следует использовать следующий синтаксис:\ let [fire, clock, , watermelon] = emojis;
Это то же самое, что и "let fire = emojis[0];", но способ гораздо более гибкий. Вы обратили внимание, что я пропустил присваивание смайлика "награда", просто оставив пустое пространство между запятыми? Итак, что у нас получится? console.log(fire, clock, watermelon);
Результат: Думаю, здесь также стоит упомянуть оператор "rest". Если вы хотите деструктурировать массив, присвоив один или более элементов переменным, а все оставшиеся элементы поместить в другой массив, это можно реализовать с помощью параметра "...rest", как показано ниже. let [fruit, ...rest] = emojis;
console.log(rest); Результат: Объект Подобно массивам, деструктурировать можно и объекты let shape = {
name: 'rect', sides: 4, height: 300, width: 500 }; Деструктурируя объект подобным образом, мы присваиваем переменным значения двух первых свойств объекта, а остальные помещаем в другой объект. let {name, sides, ...restObj} = shape;
console.log(name, sides); console.log(restObj); Результат Дополнительно почитать на тему деструктуризации можно здесь. 12. Обмен значений переменных С помощью техники деструктуризации, с которой мы ознакомились выше, это сделать очень просто let fire = '';
let fruit = ''; [fruit, fire] = [fire, fruit]; console.log(fire, fruit); 13. isArray Ещё один полезный метод, на этот раз позволяющий определить, являются ли входящие данные массивом let emojis = ['', '️', '', ''];
console.log(Array.isArray(emojis)); let obj = {}; console.log(Array.isArray(obj)); 14."'undefined" против "null" "undefined" – присутствует у переменной, которая была объявлена, но значение для неё задано не было "null" – обозначает пустое или несуществующее значение, которое явно присваивается переменной "undefined" и "null" не равны при строгом сравнивании undefined === null // false
Дополнительно почитать на тему разницы между этими двумя значениями можно здесь 15. Получение фрагментов url-адреса Объект "window.location" имеет набор полезных методов и свойств. С помощью них мы можем получить данные про протокол, хост, порт, домен и тому подобное из url-адреса браузера. Одно из свойств, которое мне кажется очень полезным window.location.search
Свойство "search" возвращает фрагмент строки url-адреса, находящийся после вопросительного знака: "?project=js". Для получения параметров запроса, помимо "location.search" можно использовать ещё один полезный API, называемый "URLSearchParams". let project = new URLSearchParams(location.search).get('project');
В результате получаем "js" Подробнее об этой теме читайте здесь Это не конец Существует множество других полезных приёмов. Я решил добавлять их в репозиторий вместе с небольшими примерами по мере того, как буду с ними сталкиваться. Какие у вас любимые приёмы JavaScript? Как насчёт поделиться ими в комментариях ниже? =========== Источник: habr.com =========== =========== Автор оригинала: Tapas Adhikary ===========Похожие новости:
Разработка веб-сайтов ), #_javascript |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 26-Ноя 15:02
Часовой пояс: UTC + 5