ЧАСТИНА 3
March 22, 2024
Аліса Лісняк
Full Stack web-розробник. Ментор навчальної програми 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 - це хороша мова для старту новачків, оскільки вона проста і не потребує специфічних знань для її опанування. Навчитись програмувати на джаваскрипт можна швидше, ніж іншими мовами, хоча це, звісно, залежить від обраного темпу та формату навчання. А найголовніше, що потрібно щоб навчитися програмувати - це практика, практика і ще раз практика!