Среда выполнения js, что это такое

Обновлено: 20.11.2024

Для создания динамических веб-приложений AJAX необходимы базовые знания об асинхронном программировании. Большинство разработчиков внешнего интерфейса выполняют асинхронное программирование при работе с JavaScript, и это связано с характером среды выполнения JavaScript.

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

Прежде чем продолжить, вы должны знать о следующих трех вычислительных концепциях:

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

Поэтому сбой программы с бесконечными рекурсивными вызовами функций называется переполнением стека/буфера. В стеке было так много вызовов функций, что ему не хватило памяти.

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

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

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

Но тогда вы можете задаться вопросом, как работают веб-приложения, если интерпретатор может выполнять только одну операцию за раз? Что ж, хотя JavaScript является однопоточным, он выполняет задачи параллельно.

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

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

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

Вызов функции Время Пример
Обычная функция call Прямо в стек вызовов fun()
В задаче Переходит к задаче очередь, а затем в стек вызовов Веб-API, такие как setTimeout(fun, 1000);
В микрозадаче Переходит к очередь микрозадач, а затем в стек вызовов После разрешения промиса, например, fetch().then(fun);

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

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

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

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

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

Выйти из полноэкранного режима

Поскольку fetch является асинхронным вызовом, функция, которая присваивает myresponse ответу, помещается в очередь задач и выполняется ПОСЛЕ handleResponse(). Следовательно, handleResponse() будет вызываться со значением 'loading. ' вместо фактического ответа.

Если вам нужно что-то сделать с выводом асинхронного вызова, это должно быть сделано в рамках задачи.

Выйти из полноэкранного режима

Его можно сократить еще больше, потому что handleResponse() принимает только один параметр.

Выйти из полноэкранного режима

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

Анимация в публикации была создана с помощью замечательного инструмента JS Visualizer 9000.

Node.js — это платформа, созданная на основе среды выполнения JavaScript в Chrome и позволяющая легко создавать быстрые и масштабируемые сетевые приложения. Node.js использует управляемую событиями неблокирующую модель ввода-вывода, которая делает его легким и эффективным и идеально подходит для приложений реального времени с интенсивным использованием данных, которые работают на распределенных устройствах.

Может ли кто-нибудь помочь мне понять, что именно означает время выполнения JavaScript?

Среда выполнения JavaScript — это где выполняется ваш код JavaScript, когда вы его запускаете. Тем не менее, javascript может быть выполнен в google chrome, и в этом случае ваша среда выполнения javascript будет v8, если в mozilla - это spidermonkey, если в IE - тогда его чакра, а если на узле, снова его v8.

4 ответа 4

Возможно, это относится к движку Google V8.

  • Это механизм обработки JavaScript в браузере, используемый Google Chrome.
  • Это открытый исходный код.
  • И он написан на C++.
  • Он работает на нескольких платформах, включая мобильные и встроенные устройства.

Если вы погуглите "среда выполнения Chrome javascript", вы получите все эти ссылки на V8.

Среда выполнения javascript для Chrome — это движок Google V8, разработанный Google для использования с Google Chrome.

Он компилирует код javascript в собственный машинный код вместо того, чтобы интерпретировать байт-код, что значительно повышает производительность javascript (который традиционно очень медленный по сравнению с другими языками высокого уровня).

Node.js содержит libuv для обработки асинхронных событий. V8 обеспечивает среду выполнения для JavaScript.

Это виртуальная машина, которая интерпретирует и выполняет JavaScript в основном в браузере. На самом деле Node.js — это библиотека на основе среды выполнения JavaScript.

API JavaScript Runtime (JsRT) позволяют приложениям для настольных компьютеров, Магазина Windows и серверным приложениям, работающим в операционной системе Windows, добавлять возможности сценариев в приложение с помощью основанного на стандартах механизма JavaScript Chakra, который также используется. Microsoft Edge и Internet Explorer. Эти API доступны в Windows 10 и любой версии операционной системы Windows, на компьютере которой установлен Internet Explorer версии 11.0.

Я не понимаю -2 голоса по этому поводу. Это из-за Internet Explorer, я использовал его, это отличный браузер.

Что происходит «под прикрытием» JavaScript и среды выполнения

Большинство разработчиков, возможно, слышали о стеке вызовов или движке V8 или знали, что JavaScript является однопоточным языком, когда в нем используется очередь обратного вызова. Учитывая, что многое из того, что происходит в браузере, зависит от JavaScript, сколько людей действительно знают, что происходит «под прикрытием»? Используем ли мы лучшее из всего, что могут предложить язык и среда? Есть ли у нас достаточно глубокое понимание внутреннего устройства, чтобы помочь нам создавать отличное программное обеспечение и радовать наших пользователей?

Что такое среда выполнения?

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

В контексте браузера он состоит из следующих элементов:

  1. движок JavaScript (который, в свою очередь, состоит из кучи и стека вызовов)
  2. Веб-API
  3. Очередь обратного вызова
  4. Цикл событий

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

движок JavaScript

Чтобы начать писать JavaScript, нам не нужно устанавливать какое-либо специальное программное обеспечение, потому что у каждого веб-браузера есть собственная версия движка JS, который анализирует код для нас. Chrome использует движок V8 JS, разработанный Chromium Project. Firefox использует SpiderMonkey, который был впервые написан Бренданом Эйхом из Netscape, а теперь поддерживается людьми из Mozilla.

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

В этой статье мы сосредоточимся на движке JavaScript V8.

Куча

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

Стек вызовов

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

Когда движок JS входит в функцию, она помещается в стек. Когда функция возвращает значение или отправляется в веб-API, она извлекается из стека. Если функция явно не возвращает значение, движок вернет значение undefined и также вытолкнет функцию из стека. Именно это подразумевается под термином «JavaScript выполняется синхронно»; он однопоточный, поэтому может выполнять только одну операцию за раз.

Веб-API

Веб-API не являются частью механизма JavaScript, но являются частью среды выполнения, предоставляемой браузером. В современных браузерах доступно большое количество API, которые позволяют нам делать самые разные вещи. Некоторые из наиболее распространенных категорий браузерных API позволяют нам:

  • Управление документами. Одним из наиболее распространенных веб-API является DOM API, который позволяет разработчикам манипулировать HTML и CSS, позволяя нам создавать, изменять и даже удалять HTML и динамически применять стили к нашим веб-страницам.
  • Рисование и управление графикой. Широко поддерживаемые браузерами Canvas API и API библиотеки веб-графики позволяют программно обновлять пиксельные данные, содержащиеся в элементе.
  • Извлечение данных с сервера. Fetch API предоставляет интерфейс для получения ресурсов по сети с помощью общих определений объектов запроса и ответа.

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

Очередь обратного вызова

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

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

Цикл событий

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

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

Сводка

  • Среда выполнения — это то, что заставляет код JavaScript работать, а в браузере она состоит из движка JS, множества веб-API, очереди обратного вызова и цикла обработки событий.
  • Подсистема JS преобразует исходный код в машинный код, что позволяет компьютеру выполнять определенные задачи на аппаратном уровне.
  • Веб-API расширяют возможности языка JS и передают функции обратного вызова в очередь обратных вызовов после завершения действий и получения данных
  • Очередь обратного вызова хранит функции обратного вызова по порядку, готовые к выполнению
  • Цикл событий постоянно отслеживает стек вызовов и очередь обратных вызовов; если стек вызовов пуст, он переместит функцию обратного вызова из начала очереди в стек вызовов, запланировав ее выполнение

На создание этой статьи меня вдохновил доклад Филлипа Робертса под названием "Что, черт возьми, такое цикл событий?" на JSConf ЕС. Это побудило меня «копаться под капотом», чтобы узнать немного больше о том, как на самом деле работает JavaScript, и я чувствую, что в долгосрочной перспективе это сделает меня лучшим разработчиком.

В среде выполнения будет выполняться ваша программа. Он определяет, к каким глобальным объектам может обращаться ваша программа, а также может влиять на то, как она работает. В этой статье рассматриваются две среды выполнения JavaScript:

  1. среда выполнения браузера (например, Chrome или Firefox)
  2. среда выполнения Node

Среда выполнения браузера

Код JavaScript чаще всего выполняется в браузере. Например, с помощью любого текстового редактора вы можете создать на своем компьютере файл с именем my_website.html и поместить в него следующий HTML-код:

Сохраните файл, а затем откройте свой любимый браузер.Большинство браузеров позволяют загружать веб-сайты, которые вы создали локально, перейдя в меню «Файл» > «Открыть файл» > «my_website.html».

После загрузки встроенное приложение будет выполнено, и метод window.alert() создаст в вашем браузере всплывающее окно с текстом "Hello World" . Как это возможно? Откуда взялся метод window.alert() и как он может управлять вашим браузером?

Ответ заключается в том, что вы выполняете этот код в среде выполнения браузера. Метод window.alert() встроен в эту среду, и любая программа, выполняемая в браузере, имеет доступ к этому методу. На самом деле объект окна предоставляет доступ к огромному количеству данных и функций, связанных с открытым окном браузера, помимо .alert() .

Попробуйте заменить window.alert() на window.prompt() или window.confirm()

Приложения, созданные и выполняемые в браузере, называются интерфейсными приложениями. Долгое время код JavaScript мог выполняться только в браузере и использовался исключительно для создания интерфейсных приложений. Для создания внутренних приложений, которые могли бы работать на компьютере БЕЗ браузера, вам потребуется использовать другие языки программирования, такие как Java или PHP.

Среда выполнения узла

В 2009 году была создана среда выполнения Node для выполнения кода JavaScript без браузера, что позволило программистам создавать полный стек (интерфейсный и внутренние) приложения, использующие только язык JavaScript.

Node — это совершенно другая среда выполнения, а это означает, что нельзя использовать значения данных и функции среды браузера, такие как window.alert() . Вместо этого среда выполнения Node предоставляет серверным приложениям доступ к различным функциям, недоступным в браузере, таким как доступ к файловой системе сервера, базе данных и сети.

Например, предположим, что вы создали файл с именем my-app.js. Мы можем проверить каталог, в котором находится этот файл, с помощью процесса переменной среды выполнения Node:

Обратите внимание, что теперь мы используем console.log вместо window.alert(), поскольку объект окна недоступен

процесс — это объект, содержащий данные, относящиеся к выполняемому файлу JavaScript. process.env — это объект, содержащий переменные среды, такие как process.env.PWD, который содержит текущий рабочий каталог (и означает «Печать рабочего каталога»).

Чтобы выполнить код JavaScript в этом файле, сначала убедитесь, что вы настроили Node на своем компьютере. Затем откройте терминал и выполните следующую команду:

Команда node указывает вашему компьютеру выполнить файл my-app.js в среде Node. Вы также можете использовать команду node без аргумента файла, чтобы открыть Node Read-Eval-Print-Loop (REPL):

Обзор

В среде выполнения будет выполняться ваша программа. Код JavaScript может выполняться в одной из двух сред выполнения:

  1. среда выполнения браузера
  2. среда выполнения Node

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

Инструменты

Масштабируемость, задержка и пропускная способность являются ключевыми показателями производительности веб-серверов. Поддерживать низкую задержку и высокую пропускную способность при масштабировании вверх и вниз непросто. Node.js — это среда выполнения JavaScript, которая обеспечивает низкую задержку и высокую пропускную способность за счет «неблокирующего» подхода к обслуживанию запросов. Другими словами, Node.js не тратит время и ресурсы на ожидание возврата запросов ввода-вывода.

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

[ Также на InfoWorld: обзор 10 лучших редакторов JavaScript ]

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

Node.js оказывается весьма полезным не только для серверов, но и для настольных приложений. Также обратите внимание, что приложения Node не ограничиваются чистым JavaScript.Вы можете использовать любой язык, который транспилируется в JavaScript, например TypeScript и CoffeeScript. Node.js включает механизм JavaScript Google Chrome V8, который поддерживает синтаксис ECMAScript 2015 (ES6) и не требует транспилятора ES6-в-ES5, такого как Babel.

Большая часть полезности Node связана с его большой библиотекой пакетов, доступ к которой можно получить с помощью команды npm. NPM, диспетчер пакетов Node, является частью стандартной установки Node.js, хотя у него есть собственный веб-сайт.

Немного истории JavaScript

В 1995 году Брендан Эйх, в то время работавший по контракту с Netscape, создал язык JavaScript для работы в веб-браузерах. По легенде, за 10 дней. Первоначально JavaScript предназначался для включения анимации и других манипуляций с объектной моделью документа браузера (DOM). Вскоре после этого была представлена ​​версия JavaScript для Netscape Enterprise Server.

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

Изначально многие программисты считали JavaScript бесполезным для «реальной работы», потому что его интерпретатор работал на порядок медленнее, чем компилируемые языки. Ситуация изменилась, когда несколько исследовательских усилий, направленных на ускорение JavaScript, начали приносить плоды. Наиболее заметно то, что движок JavaScript Google Chrome V8 с открытым исходным кодом, который выполняет компиляцию, встраивание и динамическую оптимизацию кода точно в срок, может фактически превзойти код C++ для некоторых нагрузок и превосходит Python для большинства случаев использования.

Компания Joyent много лет владела, руководила и поддерживала разработку Node.js. В 2015 году проект Node.js был передан Node.js Foundation и стал управляться техническим руководящим комитетом фонда. Node.js также рассматривался как совместный проект Linux Foundation. В 2019 году Node.js Foundation и JS Foundation объединились в OpenJS Foundation.

[ Также на InfoWorld: обзор 6 лучших интегрированных сред разработки JavaScript ]

Базовая архитектура Node.js

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

Библиотека Node.js

IDG

Одной из присущих однопоточному циклу обработки событий проблем является отсутствие вертикального масштабирования, поскольку поток обработки событий будет использовать только одно ядро ​​ЦП. Между тем, современные чипы ЦП часто имеют восемь или более ядер, а современные серверные стойки часто имеют несколько чипов ЦП. Однопоточное приложение не сможет в полной мере использовать более 24 ядер в надежной серверной стойке.

Это можно исправить, хотя для этого потребуется дополнительное программирование. Начнем с того, что Node.js может создавать дочерние процессы и поддерживать каналы между родительским и дочерними процессами, аналогично тому, как работает системный вызов popen(3), используя child_process.spawn() и связанные методы.

Модуль кластера даже более интересен, чем модуль дочернего процесса для создания масштабируемых серверов. Метод cluster.fork() порождает рабочие процессы, которые совместно используют порты родительского сервера, используя скрытую функцию child_process.spawn(). Мастер кластера распределяет входящие подключения между своими рабочими процессами, используя по умолчанию алгоритм циклического перебора, чувствительный к нагрузке рабочих процессов.

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

[ Также в InfoWorld: TypeScript и JavaScript: различия ]

Экосистема пакетов Node.js

Реестр NPM содержит более 1,2 миллиона пакетов бесплатного повторно используемого кода Node.js, что делает его крупнейшим реестром программного обеспечения в мире. Обратите внимание, что большинство пакетов NPM (фактически это папки или элементы реестра NPM, содержащие программу, описанную в файле package.json) содержат несколько модулей (программ, которые вы загружаете с помощью операторов require). Эти два термина легко спутать, но в данном контексте они имеют особое значение и не должны меняться местами.

NPM может управлять пакетами, которые являются локальными зависимостями определенного проекта, а также глобально установленными инструментами JavaScript. При использовании в качестве диспетчера зависимостей для локального проекта NPM может одной командой установить все зависимости проекта через файл package.json. При использовании для глобальной установки NPM часто требуются системные привилегии (sudo).

Вы не обязаны использовать командную строку NPM для доступа к общедоступному реестру NPM. Другие менеджеры пакетов, такие как Facebook Yarn, предлагают альтернативный клиентский опыт. Вы также можете искать и просматривать пакеты на веб-сайте NPM.

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

Параметр —save, который включен по умолчанию в NPM 5.0 и более поздних версиях, указывает диспетчеру пакетов добавить модуль Express в список зависимостей в файле package.json после установки.

Еще один быстрый способ начать использовать Express — установить исполняемый файл generator express(1) глобально, а затем использовать его для локального создания приложения в новой рабочей папке:

После этого вы можете использовать NPM для установки всех необходимых зависимостей и запуска сервера на основе содержимого файла package.json, созданного генератором:

Сложно выделить самые важные из миллионов пакетов в NPM, но несколько категорий выделяются. Express — старейший и наиболее известный пример фреймворка Node.js. Еще одна большая категория в репозитории NPM — это утилиты для разработки JavaScript, в том числе browserify, сборщик модулей; Bower, менеджер пакетов браузера; grunt, средство выполнения задач JavaScript; и gulp, система потоковой сборки. Наконец, важной категорией для корпоративных разработчиков Node.js являются клиенты баз данных, которых существует более 8 000, включая популярные модули, такие как redis, mongoose, firebase и pg, клиент PostgreSQL.

[ Будьте в курсе последних событий в области разработки программного обеспечения, облачных вычислений, анализа данных и машинного обучения с помощью информационного бюллетеня InfoWorld Daily ]

Подводя итог, Node.js – это кроссплатформенная среда выполнения JavaScript для серверов и приложений. Он построен на основе однопоточного неблокирующего цикла событий, механизма JavaScript Google Chrome V8 и низкоуровневого API ввода-вывода. Различные методы, в том числе модуль кластера, позволяют приложениям Node.js масштабироваться за пределы одного ядра ЦП. Помимо своей основной функциональности, Node.js вдохновил экосистему из более чем миллиона пакетов, которые зарегистрированы и имеют версию в репозитории NPM и могут быть установлены с помощью командной строки NPM или альтернативы, такой как Yarn.

Мартин Хеллер — пишущий редактор и обозреватель InfoWorld. Ранее он был консультантом по веб-программированию и программированию для Windows. С 1986 по 2010 год он разрабатывал базы данных, программное обеспечение и веб-сайты. В последнее время он занимал должность вице-президента по технологиям и обучению в Alpha Software, а также председателя и генерального директора Tubifi.

Читайте также: