Login

«Строгий» 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 его знаменитой гибкости, при этом добавляют полезной организованности как коду, так и разработчику.

Похожие статьи:
Практический воркшоп на 2.5 часа, где вы познакомитесь с основами языка программирования R, анализа данных и машинного обучения....
  YouTube планирует запустить свой подписной VOD-сервис. Это модель, которая позволит индивидуально доставлять каждому абоненту...
Привет, меня зовут Егор, я из Киева и мне 24 года. Уже пять лет живу в Лондоне. В 2017 закончил бакалавриат Computer Science в University...
Меня зовут Евгений и более 5 лет я занимался решениями в области Building Information Modelling (BIM), CAD- и CAE-решениями...
Компания Lenovo анонсировала для российского рынка новые доступные ноутбуки IdeaPad 100S с диагоналями экранов...
Switch to Desktop Version