ЧАСТЬ 3

March 21, 2024

Алиса Лисняк

Full Stack веб-разработчик. Ментор учебной программы Full Stack JavaScript

Ключевые характеристики и функциональность JavaScript

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

Манипуляция DOM

Для того чтобы джаваскрипт мог взаимодействовать с элементами, браузер предоставляет разработчикам особый интерфейс - Document Object Model (DOM), то есть объектную модель документа. Она определяет, что все элементы на странице являются объектами, связанными иерархической системой.

Например, имеем страницу с такой-то html-разметкой:

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

Каждый "родительский" объект в этой схеме имеет ссылку на "дочерние", а точкой входа для всего интерфейса DOM является объект document, представляющий всю веб-страницу.

Объект document предоставляет ряд методов, необходимых для получения ссылки на существующие объекты на странице на основе определенных селекторов (маркеров элемента, позволяющих выделить именно этот элемент из потока всей страницы. Ранее существовали только не очень удобные методы getElementById, getElementsByClassName, getElementsByTagName и другие, но впоследствии их заменили более универсальные querySelector и querySelectorAll, которые принимают любые валидные css-селекторы и даже их комбинации.

Имея ссылку на объект – можем делать с ним что угодно. Например, можно динамически изменять свойства элементов (цвета, расположение элементов, их позиционирование, адреса картинок и многое другое). Также объект document позволяет создавать новые элементы и размещать их на странице.

События и обработка событий

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

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

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

Асинхронное программирование

Javascript это однопоточный язык программирования. Это означает, что он не может параллельно выполнять несколько задач одновременно, только одну за другой. Но при этом js может выполняться асинхронно. Как?

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

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

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

JavaScript: где можно использовать?

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

Создание интерактивных и динамических веб-интерфейсов

Веб-интерфейс – это та часть приложения, с которой сталкивается конечный пользователь, то есть обычный человек, который заходит на сайт, магазин, платформу и т.д. Фронтенд создается с помощью языка разметки (html), стилей (css) и программирования (js). Именно здесь язык программирования JavaScript показывает себя во всей красе - с помощью DOM мы можем динамически создавать и изменять элементы на странице, делать запросы на сервер, обрабатывать данные и взаимодействовать с пользователем.

Серверные программы

Одним из ключевых моментов истории джаваскрипт стало появление серверной платформы Node.js, написанной Райаном Далом в 2009 году. Его разработка была не первой серверной средой для JavaScript, но наиболее удачной и удобной. Именно Node.js открыла для js-разработчиков возможность создавать не только клиентскую часть приложений (frontend, то есть часть, работающую в браузере пользователя) но и серверную (backend, то есть логическая часть, работающая "за кулисами" приложения и которую обычные пользователи не видят, но именно там проходит основная работа с данными).

Библиотеки и фреймворки

Позже стали появляться js-фреймворки. Фреймворк – это определенная программная платформа, каркас, позволяющий разработчикам быстро и легко создавать приложения без лишнего “шаблонного кода” – потребности писать одно и то же, чтобы выполнять типовые задачи.

Фреймворки JavaScript существуют разные – некоторые помогают создавать клиентскую часть, то есть фронтенд (React.js, Angular, Vue и другие), некоторые упрощают написание бэкенд-части (Express, Nest.js). Остальные помогают создавать кросс-платформенные приложения и десктопные программы (Electron, Proton Native) и даже мобильные приложения для смартфонов(React Native). Выходит, что можно сделать на javascript и фронтенд, и бэкенд, и мобильное приложение, а также десктопные приложения.

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

JavaScript: проблемы и лучшие практики написания кода

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

Проблемы, с которыми сталкиваются разработчики JS, и способы их избежания

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

Основное решение этой проблемы – проверка и явная конвертация типов. Вместо операторов типа "унарного плюса" или "логического НЕ" следует конвертировать операнды с помощью функций-конструкторов соответствующего типа данных:

В 2012 компания Microsoft представила другую реализацию стандарта EcmaScript - язык TypeScript, которая позиционировалась как "надстройка" над JavaScript, которая предлагала статическую типизацию и другую реализацию классов. Но не все пошло, как планировалось, и с этим языком многое получилось “не так” - от усложнения читаемости кода до путаницы с описаниями типов и отсутствия поддержки многих синтаксических упрощений, активно появляющихся в JavaScript. К тому же этот язык точно не рассчитан на новичков в программировании и скорее еще больше запутывает, чем помогает на первых шагах.

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

Стратегии написание эффективного кода JavaScript

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

Например, принцип "DRY" (“Don't repeat yourself”) напоминает о необходимости написания кода таким образом, чтобы вместо “повторения” логики - переиспользовать ее. Вместо неоднократного выполнения одних и тех же операций следует писать функции, выполняющие одну работу с разными данными или имеющие определенные "настройки" работы, приходящие вместе с входными данными, а вместо дублирования методов в объектах - подумать о наследовании.

Еще одним важным подход чистого программирования является "Принцип единой ответственности" (Single Responsibility Principle, SRP), который распространяется не только на объектно-ориентированное программирование, но и на написание кода в целом. Этот принцип говорит нам о том, что каждый метод и каждая функция имеет свою "зону ответственности" и выполнять конкретную логическую работу (не обязательно только одно действие, но именно один блок логической работы). К примеру, нам нужно принять от пользователя данные, провалидировать (проверить их на правильность), превратить в формат, нужный для пересылки и отправить на сервер. Принцип SRP говорит нам, что каждую из этих частей работы должна выполнять своя функция - отдельно для валидации, отдельно для пересылки, а не делать абсолютно всё в одной функции.

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

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

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

Потенциальные уязвимости и лучшие методы защиты приложений JavaScript

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

Первые и самые простые виды уязвимостей в JavaScript – это устаревшая функция eval, использование которой не рекомендуется вообще. Когда-то давно она использовалась, чтобы частично встраивать js код в html-разметку, и ее принцип работы очень прост - она ​​принимает любую строчку кода и выполняет его как JavaScript. Ее использование открывает безграничное пространство для злонамеренного кода, поэтому сейчас это считается "плохим тоном" и эта функция вообще не используется.

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

Ранее, до ES6, единственным способом создания переменных было ключевое слово var, создававшее глобальные переменные без "области видимости", и позволяло переприсвоить ей позже другое значение. Спецификация EcmaScript 2015 принесла два новых ключевых слова - let для переменных и const для констант (одноразовых переменных, значение которых невозможно переопределить). Оба ключевых слова создают переменные, видимые в пределах блока кода, где были определены, или ниже, таким образом с помощью них и особого механизма под названием "замыкание" можно изолировать значения переменных и не допустить воздействие на них извне.

Более сложный тип уязвимости - межсайтовый скриптинг или XSS (Cross-site scripting) - инъектирование вредоносного кода на страницы, которые сервер посылает пользователям, с целью похищения их личной информации.

Первая "линия обороны" для защиты от этого вида атак - экранирование, проверка введенных данных и предварительный парсинг всего, что посылается с клиентских страниц на сервер. Второе, безусловно, использование политики SOP (Same-Origin Policy), не позволяющей запускать скрипты из "чужеродного" источника или CSP (Content Security Policy), которая устанавливает список "доверенных" источников, а все остальные просто игнорируются.

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

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

Перспективы развития ДжаваСкрипт

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

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

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

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