«Суворий» JavaScript: навіщо і кому це треба
Олена Жукова , підприємець і Frontend developer, на VinnytsiaJS виступила з доповіддю «Strict JavaScript» і на його основі написала статтю для DOU. JavaScript вважається динамічним мовою, але все частіше використовуються інструменти, які додають йому статичної типізації. Google, Facebook і Microsoft пропонують свої рішення. Чому так відбувається і чи варто це робити?
Дані з GitHub за останні 3 роки показують істотне зростання кількості проектів, написаних за допомогою інструментів, які додають в JavaScript статичної типізації. Звичайно, можна припустити, що використання, наприклад, TypeScript сприяв вихід фреймворку Angular2, який настійно рекомендував використовувати саме TypeScript, проте існує чимало проектів, які використовують зовсім інший фреймворк — ReactJS, при цьому написані на TypeScript. До того ж Facebook розробив власний інструмент для статичної типізації JavaScript — Flow, яким рекомендує користуватися для свого фреймворку ReactJS.
Таке широке поширення статичних надбудов над динамічним JavaScript-му змушує задуматися: «Навіщо і кому це треба?».
Невеликий екскурс в історію появи JavaScript
Ми всі знаємо, що JavaScript був створений в 1995 році Бренданом Эйхом для браузера Нетскейп навігатор. Автор говорив, що на його роботу вплинули такі популярні в той час мови програмування як Scheme, Self, Java і Smalltalk. Примітно, що всі ці мови мають строгу типізацію. Так чому ж JavaScript вийшов таким слабо типізованих?
Можливо, цитата автора допоможе це зрозуміти:
«HTML потребував „мовою сценаріїв“, мовою програмування, який був би простий у використанні любителями і новачками, де код міг бути написаний безпосередньо у вихідній формі як частину розмітки веб-сторінки. Ми прагнули надати „мова-прошарок“ для веб-дизайнерів і програмістів-аматорів, які будують веб-контент з таких компонентів, як зображення, плагіни і аплети Java. Ми розглядали Java як „компонентний мова“, використовується більш досвідченими програмістами, тоді як програмісти прошарку — розробники веб-сторінок — збирали б компоненти і автоматизували б їх взаємодії з допомогою JS».Брендан дуже б здивувався, якби йому тоді сказали, що мине 20 років, і всі браузери відмовляться від використання Java аплетів, а JavaScript стане найпопулярнішою мовою.
JavaScript залишався несерйозним мовою для веб-дизайнерів до 2004 року, коли Google, використовуючи технологію AJAX, випустили в масове споживання Gmail. Для свого поштовика Google вже не використовували чистий JS. Вони придумали набір інструментів Google Web Toolkit , який перетворював Java в JavaScript. Основною метою GWT заявляв кросбраузерність, свободу від вивчення JavaScript, а також загальний код для бекенду і фронтенда (single code base).
Щороку яка-небудь компанія придумувала свої інструменти перетворення інших мов в JavaScript. Їх більше сотні , і їх об'єднують цілі і завдання, які ставив перед собою і GWT.
Всі ці рішення об'єднувала одна особливість — вони були створені для програмістів, які більшу частину часу працювали з іншими мовами і не хотіли переходити на JavaScript.
У 2011 році з'явився TypeScript — відносно самостійний мову (під сильним впливом C#) спеціально для JavaScript-розробників, головною задачею якого була саме статична типізація для JS. У 2014 подібне рішення — Flow — представили і в Facebook.
Аргументи «за»
Так чому ж гіганти індустрії Google, Microsoft і Facebook займаються однією справою — додають статичної або суворої типізації JavaScript?
Тут можна вказати на кілька причин.
Знайти помилки на етапі компіляції
JavaScript не вимагає компіляції, а слабка динамічна типізація робить практично неможливим аналіз коду для IDE. З цієї причини ми можемо зіткнутися з помилкою, пов'язаною саме з типами даних, тільки у певних випадках під час роботи програми. Це ускладнює процес налагодження продукту.
Навіть самий досвідчений розробник JavaScript не завжди може сказати, яким буде результат виконання наступних операцій:
/* * JavaScript coercion */ var objArr = {}+[]; var arrObj = []+{}; var arrNum = []+1; var arrNum = []*1; var arrNum1 = [2]*2; var arrNum2 = [2,1]+2; var arrNum3 = [2,1]-2; var arrs = [1]+[2];
У таких випадках хотілося б отримувати повідомлення про помилку вже в процесі написання коду навіть в самому простому редакторі, як це дозволяє TypeScript або Flow з безкоштовним редактором Atom.
Більш читабельний код
Часто статичні мови критикують за велику кількість покажчиків на тип даних, які нібито заважають читабельність коду. Однак типи явно, а головне, завжди правильно вказують розробнику на правила використання коду.
Ось простий приклад коду. Досить простий, щоб розробник вирішив, що тут все гранично зрозуміло: є продукт, у нього є категорія, навіщо тут вказувати типи?
function Product(){ } Product.prototype.setCategory = function (category) { this.category = category; };
Однак коли колега автора цього коду або сам автор через деякий час вирішує написати функцію пошуку по категорії продукту:
function searchProductsByCategory(products, category) { return products.filter(product=>{ return //??? }) }
Тут виникає проблема: «А що таке категорія? І як написати порівняння?». Може бути, що категорія — це рядок або число, або об'єкт...
TypeScript або Flow дозволяють нам явно вказати тип параметра і тим самим однозначно вирішити подібні питання.
interface Category{ name: string; id: string; } class Product{ private category:Category; setCategory(category:Category){ this.category = category; } }
Код, яким легше керувати
Зміни в коді — звичайна рутина для програміста. Дуже важливо, щоб ця робота відбувалася максимально безболісно, але JavaScript — не той випадок. При достатньо великому обсязі коду, особливо розділеному на кілька файлів, або занадто загальних назвах змінних зміни в коді можуть бути дуже нетривіальною завданням. Рефакторинг стає значно простіше, якщо використовувати TypeScript або Flow. Ваш редактор може автоматично змінити назву змінної або методу у всіх місцях, де ви їх використовуєте.
Аргументи «проти»
Зазначених трьох причин достатньо для того, щоб значно полегшити життя розробника, проте є й інший погляд на це питання. Є аргументи проти використання статичної типізації для JavaScript.
У передмові до книги Кайла Сімпсона «Ви не знаєте JavaScript: типи та граматика» Девід Волш пише:
«JavaScript — єдина мова, яку розробники не вчать, перед тим як почати використовувати».Противники суворого JavaScript кажуть, що люди користуються інструментами для статичної типізації просто тому, що вони не знають, як користуватися JS .
Наприклад, Ерік Елліот каже :
«В динамічно типизированном мові немає необхідності в конструкторах типів... Замість цього розробники можуть використовувати duck typing і, можливо, виконувати перевірки типи часу виконання».В якості прикладу Елліот пропонує таку функцію для перевірки наявності обов'язкових параметрів:
const fn = (fn, {required = []}) => (params = {}) => { const missing = required.filter(param => !(param in params); if (missing.length) { throw new Error(`${ fn.name }() Missing required parameter(s): ${ missing.join(', ') }`); } return fn(params); }; const createEmployee = fn( ({ name = ", hireDate = Date.now(), title = 'Worker Drone' } = {}) => ({ name, hireDate, title }), { required: ['name'] } ); console.log(createEmployee({ name: 'foo' })); // works createEmployee(); // createEmployee() Missing required parameter(s): name
Крім того, що ця функція об'ємна і складна для читання, неймовірно дивно, що вона покликана вирішити завдання, з якою TypeScript або Flow справляється без зайвих зусиль з боку програміста, тобто просто повідомити про відсутність необхідного параметра.
Дуглас Крокфорд, автор знаменитої книги «JavaScript. Сильні сторони», так висловлюється про статичних типах в JS:
«Я знайшов у своїй роботі, що помилки, які виявляє сувора перевірка типів, не є помилками, про які я турбуюся. З іншого боку, я знаходжу вільну типізацію ліберальної. Мені не потрібно створювати складні ієрархії класів. І мені ніколи не доводиться перетворювати або боротися з системою типів, щоб отримати те поведінка, яке я хочу».Для прикладу, Крокфорд вважає, що код, як цей, не відповідає духу JavaScript:
var answer = 42.5 | 0;
І пропонує таку конструкцію для того, щоб перетворити число з крапкою (float) в ціле число (int):
Number.method('integer', function ( ) { return Math[this < 0 ? 'ceil' : 'floor'](this); });
При всьому бажанні важко прийняти подібні альтернативи для розв'язання проблем, пов'язаних з відсутністю статичної або суворої типізації в JavaScript. Тим більше, що сучасні популярні інструменти TypeScript і Flow не позбавляють JS його знаменитої гнучкості, при цьому додають корисної організованості як кодом, так і розробника.
Опубліковано: 10/10/17 @ 10:05
Розділ javascript
Рекомендуємо:
Конкурс «Кексомания». 250к рублів
.NET дайджест #20: як влаштована аутентифікація і авторизація в ASP.NET 2.0., нововведення в ASP.NET Core, огляд GraphQL vs REST
Школа Партнеркина. Робимо сітку музичних порталів
QA дайджест #30: справжня історія терміна Bug, перезавантаження ПК під час тесту, навантажувальне тестування з Gatling з нуля
Кращий тестувальник року — про кар'єру, досягнення і про те, як стати професіоналом у QA