sonyps4.ru

Что можно делать с помощью JavaScript? Язык программирования JavaScript: информация для начинающих.

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

Назовите две наиболее важные для JavaScript-разработчика парадигмы программирования?

JavaScript – мультипарадигмальный язык, поддерживающий императивное/процедурное программирования наряду с ООП и функциональным программированием. JS поддерживает ООП с прототипным наследованием.

Желательно упомянуть:

1. Прототипное наследование (также: прототипы, объектные ссылки)

2. Функциональное программирование (также: замыкания, функции первого класса, лямбды)

Следует избегать:

Не иметь представления о парадигмах, не упомянуть прототипное ООП или функциональное программирование.

Что такое функциональное программирование?

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

Функциональное программирование – одна из основополагающих концепций JavaScript (один из двух столпов JS). Некоторые функциональные утилиты были добавлены в JavaScript в ES5.

Хорошо упомянуть:

1. Чистые функции / чистота функций

2. Избежание побочных эффектов

3. Простой состав функций

4. Примеры функциональных языков: Lisp, ML, Haskell, Erlang, Clojure, Elm, F Sharp, OCaml и т.д.

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

Следует избегать:

1. Отсутствие упоминаний чистых функций / избежание побочных эффектов

2. Не привести примеры функциональных языков

3. Не привести примеры функциональных фич JavaScript

В чем заключается разница между классовым и прототипным наследованием?

Классовое наследование: экземпляры наследуются от классов, создаются подклассовые отношения (иерархическая систематизация классов). Экземпляры реализуются через конструктор функции, через дескриптор new. Экземпляр класса может не содержать дескриптор class начиная с ES6.

Прототипное наследование: экземпляры наследуются напрямую от других объектов, реализуются через фабрики или Object.create() и экземпляры могут быть составлены из множества различных объектов для упрощения выборочного наследования. Прототипное наследование более простое и гибкое, нежели классовое.

Хорошо упомянуть:

1. Классы: тесные связи, иерархия

2. Прототипы: конкатенативное наследование, делегирование, функциональное наследование, композиция

Следует избегать:

Не указать на преимущества прототипного наследования перед классовым

Каковы плюсы и минусы функционального и объектно-ориентированного программирования?

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

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

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

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

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

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

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

Хорошо упомянуть:

1. Проблемы общих состояний, нежелательного поведения

2. Возможности ФП по радикальному упрощению кода программ

3. Разность в сложности изучения

4. Побочные эффекты и их влияние на надежность программ

5. Сложность изменения и общая хрупкость базы ОО кода в сравнении с таковой в функциональном стиле

Следует избегать:

Не упомянуть недостатки одного из подходов – каждый, кто сталкивался с одним из них, знает и о паре-другой недостатков

Когда классовое наследование – подходящий выбор?

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

Хорошо упомянуть

«…композиция объектов лучше, чем наследование классов»

Следует избегать:

Упоминание Rect Components. React.js использует дескриптор class, но не позволяет избежать подводных камней классового наследования. Вопреки популярным ответам, не нужно использовать class, чтобы пользоваться React. Такой ответ покажет не понимание как классов в JavaScript, так и Реакта.

Когда лучше использовать прототипное наследование?

Существует более, чем один тип прототипного наследования:

1. Делегирование (цепочка прототипов)

2. Конкатенация (миксины, Object.assign())

3. Функциональное наследование (не путать с функциональным программированием. Функция используется для создания замыкания для private/инкапсуляции)

Хороший ответ:

1. В ситуациях, когда модули или функциональное программирование не обеспечивают очевидного решения

2. Когда нужно собрать объект из нескольких источников

3. В любом случае, когда нужно применить наследование

Следует избегать:

1. Не знать, когда применяются прототипы

2. Не знать о миксинах или Object.assign()

Что значит «композиция объектов лучше, чем наследование классов»?

Это цитата из книги “Design Patterns: Elements of Reusable Object-Oriented Software”. Повторное использование кода должно достигаться за счет сборки малых единиц функциональности в новый объект, а не наследованием классов и созданием иерархий.

Хорошо упомянуть:

1. Избежание наследования и тесных связей

2. Избежание вытекающей из классического наследования проблемы «банан/мартышка» (нужен был банан – получили мартышку, держащую банан посреди джунглей)

Следует избегать:

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

Что такое двусторонняя связь данных и однонаправленный поток данных и в чем разница между ними?

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

Однонаправленный поток данных означает, что только модель – источник истины. Изменения в интерфейсе запускают сообщения, которые сигнализируют пользователю о намерении модели (или «store» в терминах React). Смысл в том, что данные всегда идут в одном направлении, что облегчает понимание.

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

Хорошо упомянуть:

1. React – новый канонический пример однонаправленного потока данных, так что упоминание Реакта будет хорошей идеей. Cycle.js — еще одна популярная реализация однонаправленного потока данных.

2. Angular – популярный фреймворк, использующий двустороннюю привязку.

Следует избегать:

Непонимание этих концепций, неспособность объяснить разницу между ними.

Каковы плюсы и минусы монолитной архитектуры и микросервисов?

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

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

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

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

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

Минусы микросервисов: при создании нового микросервиса может появится много внезапных проблем, которые не ожидались при проектировании. Для монолитного приложения можно использовать промежуточное программное обеспечение для решения различных сквозных проблем без особых трудозатрат.

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

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

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

Хорошо упомянуть:

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

2. Практические различия микросервисов и монолитных приложений

Следует избегать:

1. Незнание различий архитектур

2. Незнание недостатков микросервисов

3. Недооценивать преимущества масштабирования микросервисов

Что такое асинхронное программирование и почему оно важно в JS?

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

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

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

Node.js по умолчанию асинхронен, по сути, сервер проводит все время в цикле, ожидая сетевой запрос.

В JavaScript это важно, так как естественно для интерфейсов и положительно сказывается на производительности сервера.

Хорошо упомянуть:

1. Значение блокировок, влияние на производительность

2. Понимание обработчиков и почему они важны для интерфейсов

Следует избегать:

1. Непонимание терминов синхронности и асинхронности

2. Неспособность определить последствия для производительности

Теперь с синтаксисом покончено, давайте наконец перейдём к самому интересному: изучению преимуществ и недостатков использования статических типов.

Преимущество № 1: Вы можете заблаговременно находить баги и ошибки

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

Небольшой пример: предположим, что у нас небольшая функция, которая берёт радиус и вычисляет площадь:

Const calculateArea = (radius) => 3.14 * radius * radius; var area = calculateArea(3); // 28.26
Теперь если мы захотим передать функции радиус, который не является числом (типа «злоумышленник»)…

Var area = calculateArea("im evil"); // NaN
Нам вернётся NaN . Если какая-то функциональность основана на том, что функция calculateArea всегда возвращает число, то это приведёт к уязвимости или сбою. Не очень приятно, правда?

Если бы мы использовали статические типы, то определили бы конкретный тип передаваемых параметров и возвращаемых значений для этой функции:

Const calculateArea = (radius: number): number => 3.14 * radius * radius;
Попробуйте теперь передать что-нибудь кроме числа функции calculateArea - и Flow вернёт удобное и симпатичное сообщение:

CalculateArea("Im evil"); ^^^^^^^^^^^^^^^^^^^^^^^^^ function call calculateArea("Im evil"); ^^^^^^^^^ string. This type is incompatible with const calculateArea = (radius: number): number => 3.14 * radius * radius; ^^^^^^ number
Теперь у нас есть гарантия, что функция будет принимать только валидные числа на входе и возвращать результат только в виде валидных чисел.

Поскольку контролёр типов сообщает вам об ошибках прямо во время написания кода, это намного удобнее (и намного дешевле), чем поиск бага после того, как код отправлен заказчику.

Преимущество № 2: У вас появляется живая документация

Типы работают как живая, дышащая документация и для вас, и для других.

Чтобы понять каким образом, посмотрим на метод, который я однажды нашла в большой кодовой базе, с которой работала:

Function calculatePayoutDate(quote, amount, paymentMethod) { let payoutDate; /* business logic */ return payoutDate; }
На первый взгляд (и на второй, и на третий), совершенно непонятно, как использовать эту функцию.

Является ли quote числом? Или логическим значением? Платёжный метод - это объект? Или это может быть строка, которая представляет тип платёжного метода? Возвращает ли функция дату в строковом виде? Или это объект Date ?

Без понятия.

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

С другой стороны, если бы написали что-то вроде такого:

Function calculatePayoutDate(quote: boolean, amount: number, paymentMethod: string): Date { let payoutDate; /* business logic */ return payoutDate; }
то немедленно стало бы ясно, какой тип данных функция принимает, а какой тип возвращает. Это пример того, как можно использовать статические типы для сообщения того, что функция намерена делать. Мы можем сообщить другим разработчикам, чего ожидаем от них, и можем увидеть, чего они ожидают от нас. Следующий раз, если кто-то соберётся использовать эту функцию, вопросов не возникнет.

Можно поспорить, что эта проблема решается добавлением комментариев к коду или документации:

/* @function Determines the payout date for a purchase @param {boolean} quote - Is this for a price quote? @param {boolean} amount - Purchase amount @param {string} paymentMethod - Type of payment method used for this purchase */ function calculatePayoutDate(quote, amount, paymentMethod) { let payoutDate; /* .... Business logic .... */ return payoutDate; };
Это работает. Но здесь гораздо больше слов. Кроме многословности, такие комментарии в коде трудно поддерживать, потому что они ненадёжные и не имеют структуры - некоторые разработчики пишут хорошие комментарии, а другие могут написать что-то непонятное, а некоторые вообще могут забыть оставить комментарий.

Особенно легко забыть обновить комментарий после рефакторинга. С другой стороны, у аннотаций типов чётко определённый синтаксис и структура, и они никогда не устареют, потому что закодированы в самом коде.

Преимущество № 3: Устраняется обработка запутанных ошибок

Типы помогают устранить обработку в коде запутанных ошибок. Давайте вернёмся к нашей функции calculateArea и посмотрим, каким образом это происходит.

На этот раз я передам ей массив радиусов для вычисления площадей для каждого радиуса:

Const calculateAreas = (radii) => { var areas = ; for (let i = 0; i < radii.length; i++) { areas[i] = PI * (radii[i] * radii[i]); } return areas; };
Эта функция работает, но неправильно обрабатывает некорректные входные аргументы. Если мы захотим убедиться, что функция правильно обрабатывает ситуации, когда входные аргументы не являются валидными массивами чисел, то придём к функции примерно такого вида:

Const calculateAreas = (radii) => < radii.length; i++) { if (typeof radii[i] !== "number") { throw new Error("Array must contain valid numbers only"); } else { areas[i] = 3.14 * (radii[i] * radii[i]); } } return areas; };
Ого. Тут много кода для такого маленького кусочка функциональности.

А со статическими типами мы просто напишем:

): Array => < radii.length; i++) { areas[i] = 3.14 * (radii[i] * radii[i]); } return areas; };
Теперь функция действительно выглядит так, как она выглядела перед добавлением всего визуального мусора из-за обработки ошибок.

Легко понять преимущества статических типов, правда?

Преимущество № 4: Вы можете увереннее осуществлять рефакторинг

Объясню это историей из жизни. Однажды я работала с очень большой кодовой базой, и нужно было обновить метод, установленный в классе User . В частности, изменить один из параметров функции со string на object .

Я произвела изменение, но мне было страшно отправлять коммит - по всему коду разбросано так много вызовов к этой функции, что я не была уверена, что правильно обновила все экземпляры. Что если какой-то вызов остался где-то глубоко в непроверенном вспомогательном файле?

Единственный способ проверить - это отправить код и молиться, что он не взорвётся кучей ошибок.

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

Преимущество № 5: Разделение данных и поведения

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

Ещё раз посмотрим на нашу функцию calculateArea со статическими типами:

Const calculateAreas = (radii: Array): Array => { var areas = ; for (var i = 0; i < radii.length; i++) { areas[i] = 3.14 * (radii[i] * radii[i]); } return areas; };
Подумайте, как бы мы подошли к составлению этой функции. Поскольку мы указываем типы данных, то вынуждены в первую очередь думать о типах данных, которые собираемся использовать, чтобы можно было соответствующим образом установить типы для передаваемых параметров и возвращаемых значений.

Только после этого мы реализуем логику:

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

Преимущество № 6: Устранение целой категории багов

Ошибки типов во время выполнения программы - одна из самых распространённых ошибок или багов, с которыми сталкиваются JavaScript-разработчики.

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

Var appState = { isFetching: false, messages: , };
И предположим, что затем мы делаем вызов API, чтобы забрать сообщения и заполнить наш appState . Далее, у нашего приложения есть чрезмерно упрощённый компонент для просмотра, который забирает messages (указанные в состоянии выше) и отображает количество непрочитанных сообщений и каждое сообщение как элемент списка:

Import Message from "./Message"; const MyComponent = ({ messages }) => { return (

{ messages.map(message => )}
); };
Если вызов API для забора сообщений не сработал или вернул undefined , то вы столкнётесь с ошибкой типа в продакшне:

TypeError: Cannot read property ‘length’ of undefined
…и ваша программа завершится со сбоем. Вы потеряете клиента. Занавес.

Посмотрим, как могут помочь статические типы. Начнём с добавления типов Flow к состоянию приложения. Я использую псевдоним типа AppState для определения состояния:

Type AppState = { isFetching: boolean, messages: ?Array }; var appState: AppState = { isFetching: false, messages: null, };
Поскольку известно, что API для забора сообщений работают ненадёжно, то укажем для значения messages тип maybe для массива строк.

Так же как в прошлый раз, мы забираем сообщения через ненадёжный API и используем их в компоненте просмотра:

Import Message from "./Message"; const MyComponent = ({ messages }) => { return (

You have { messages.length } unread messages

{ messages.map(message => )}
); };
Но в этот момент Flow обнаружит ошибку и пожалуется:

^^^^^^ property `length`. Property cannot be accessed on possibly null value

You have {messages.length} unread messages

^^^^^^^^ null

You have {messages.length} unread messages

^^^^^^ property `length`. Property cannot be accessed on possibly undefined value

You have {messages.length} unread messages

^^^^^^^^ undefined { messages.map(message => )} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `map`. Method cannot be called on possibly null value { messages.map(message => )} ^^^^^^^^ null { messages.map(message => )} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `map`. Method cannot be called on possibly undefined value { messages.map(message => )} ^^^^^^^^ undefined
Погоди, приятель!

Поскольку мы определили messages как тип maybe , мы разрешаем ему быть null или undefined . Но это не даёт нам права проводить операции с ним (вроде.length или.map) без осуществления проверки на null , потому что если значение messages на самом деле null или undefined , то выскочит ошибка типа при попытке проведения операции с ним.

Так что вернёмся и обновим нашу функцию для просмотра примерно таким образом:

Const MyComponent = ({ messages, isFetching }: AppState) => { if (isFetching) { return } else if (messages === null || messages === undefined) { return

Failed to load messages. Try again.
} else { return (

You have { messages.length } unread messages

{ messages.map(message => )}
); } };
Теперь Flow знает, что мы учли все ситуации, где messages равно null или undefined , так что проверка типов кода завершается с 0 ошибок. Прощайте, ошибки во время выполнения программы!

Преимущество № 7: Уменьшение количества юнит-тестов

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

Например, вернёмся к нашей функции calculateAreas с динамическими типами и обработкой ошибок.

Const calculateAreas = (radii) => { // Handle undefined or null input if (!radii) { throw new Error("Argument is missing"); } // Handle non-array inputs if (!Array.isArray(radii)) { throw new Error("Argument must be an array"); } var areas = ; for (var i = 0; i < radii.length; i++) { if (typeof radii[i] !== "number") { throw new Error("Array must contain valid numbers only"); } else { areas[i] = 3.14 * (radii[i] * radii[i]); } } return areas; };
Если бы мы были прилежными программистами, то могли бы подумать о тестировании недействительных передаваемых параметров для проверки, что они корректно обрабатываются нашей программой:

It("should not work - case 1", () => { expect(() => calculateAreas()).to.throw(Error); }); it("should not work - case 2", () => { expect(() => calculateAreas(undefined).to.throw(Error); }); it("should not work - case 2", () => { expect(() => calculateAreas("hello")).to.throw(Error); });
… и так далее. Но очень вероятно, что мы забудем протестировать какие-то граничные случаи, - и наш заказчик будет тем, кто обнаружит проблему. :(

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

С другой стороны, когда нам требуется установить типы:

Const calculateAreas = (radii: Array): Array => { var areas = ; for (var i = 0; i < radii.length; i++) { areas[i] = 3.14 * (radii[i] * radii[i]); } return areas; };
… мы не только получаем гарантию, что наша цель соответствует реальности, но такие тесты попросту надёжнее. В отличие от тестов на эмпирической основе, типы универсальны и их труднее обойти.

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

Преимущество № 8: Инструмент моделирования предметной области

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

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

Итак, сначала применим псевдонимы типов для трёх платёжных методов:

Type Paypal = { id: number, type: "Paypal" }; type CreditCard = { id: number, type: "CreditCard" }; type Bank = { id: number, type: "Bank" };
Теперь можно установить тип PaymentMethod как непересекающееся множество с тремя случаями:

Type PaymentMethod = Paypal | CreditCard | Bank;
Теперь составим модель состояния нашего приложения. Чтобы не усложнять, предположим, что данные приложения состоят только из платёжных методов, доступных пользователю.

Type Model = { paymentMethods: Array };
Это приемлемо? Ну, мы знаем, что для получения платёжных методов пользователя нужно сделать запрос к API и, в зависимости от результата и этапа процесса, приложение может принимать разные состояния. В реальности, возможно четыре состояния:

1) Мы не получили платёжные методы.
2) Мы в процессе получения платёжных методов.
3) Мы успешно получили платёжные методы.
4) Мы попытались получить платёжные методы, но возникла ошибка.

Но наш простой тип Model с paymentMethods не покрывает все эти случаи. Вместо этого он предполагает, что paymentMethods всегда существует.

Хм-м-м. Существует ли способ составить модель, чтобы состояние приложения принимало одно из этих четырёх значений, и только их? Давайте посмотрим:

Type AppState = { type: "NotFetched" } | { type: "Fetching" } | { type: "Failure", error: E } | { type: "Success", paymentMethods: Array };
Мы использовали тип непересекающегося множества для установки AppState в одно из четырёх состояний, описанных выше. Заметьте, как я использую свойство type для определения, в каком из четырёх состояний находится приложение. Именно это свойство type и является тем, что создаёт непересекающееся множество. Используя его мы можем осуществить анализ и определить, когда у нас есть платёжные методы, а когда нет.

Вы также заметите, что я передаю параметризованный тип E и D в состояние приложения. Тип D будет представлять собой платёжный метод пользователя (PaymentMethod , определённый выше). Мы не установили тип E , который будет нашим типом для ошибки, так что сделаем это сейчас:

Type HttpError = { id: string, message: string };
Теперь можно смоделировать предметную область приложения:

Type Model = AppState;
В целом, подпись для состояния приложения теперь AppState , где E имеет форму HttpError , а D - это PaymentMethod . И у AppState есть четыре (и только эти четыре) возможных состояния: NotFetched , Fetching , Failure и Success .

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

Более того, код документирует сам себя - достаточно посмотреть на непересекающиеся множества, и немедленно становится понятно, как структурирован AppState.

Недостатки использования статических типов

Как и всё остальное в жизни и программировании, проверка статических типов требует некоторых компромиссов.

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

Вот некоторые из этих соображений:

Недостаток № 1: Статические типы требуют предварительного изучения

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

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

Изучение эффективного использования типов - это была половина успеха в изучении самого языка. В итоге, из-за статических типов кривая обучения Elm круче, чем у JavaScript.

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

Недостаток № 2: Можно увязнуть в многословии

Из-за статических типов программы часто выглядят более многословными и загромождёнными.

Например, вместо этого:

Async function amountExceedsPurchaseLimit(amount, getPurchaseLimit){ var limit = await getPurchaseLimit(); return limit > amount; }
Нам приходится писать:

Async function amountExceedsPurchaseLimit(amount: number, getPurchaseLimit: () => Promise): Promise { var limit = await getPurchaseLimit(); return limit > amount; }
А вместо этого:

Var user = { id: 123456, name: "Preethi", city: "San Francisco", };
Приходится писать такое:

Type User = { id: number, name: string, city: string, }; var user: User = { id: 123456, name: "Preethi", city: "San Francisco", };
Очевидно, что добавляются лишние строки кода. Но есть парочка аргументов против того, чтобы считать это недостатком.

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

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

Сложно сказать, является ли многословность реальным аргументом против типов, но стóит держать его в уме.

Недостаток № 3: Требуется время для достижения мастерства в использовании типов

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

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

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

Недостаток № 4: Статические типы могут задержать быструю разработку

Как я упоминала ранее, я слегка споткнулась о типы, когда изучала Elm - особенно когда добавляла код или делала изменения в нём. Постоянно отвлекаясь на ошибки компилятора, трудно делать работу и чувствовать прогресс.

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

Дело не только в этом. Контролёры статических типов тоже не всегда идеальны. Иногда возникает ситуация, когда вы знаете что делать, а проверка типов вмешивается и мешает.

Уверена, что я упустила какие-то ещё недостатки, но это самые важные для меня.

Нужно использовать статические типы в JavaScript или нет?


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

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

Да, есть неизбежные компромиссы статических типов, такие как излишняя многословность и необходимость тратить время на их изучение. Но типы добавляют безопасность и корректность в программы, что устраняет значимость этих «недостатков» для меня лично.

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

В конечном счёте, здесь нет универсального решения. Лично я предпочитают использовать статические типы при следующих условиях:

  1. Программа критически важна для вашего бизнеса.
  2. Программа, вероятно, подвергнется рефакторингу, в соответствии с новыми потребностями.
  3. Программа сложна и имеет много подвижных частей.
  4. Программу поддерживает большая группа разработчиков, которым нужно быстро и точно понять код.
С другой стороны, я бы отказалась от статических типов в следующих условиях:
  1. Код недолговечный и не является критически важным.
  2. Вы делаете прототип и стараетесь продвигаться как можно быстрее.
  3. Программа маленькая и/или простая.
  4. Вы единственный разработчик.
Преимущество разработки на JavaScript в наши дни состоит в том, что благодаря инструментам вроде Flow и TypeScript у нас наконец-то появился выбор - использовать статические типы или старый добрый JavaScript.

Заключение

Надеюсь, эти статьи помогли вам понять важность типов, как их использовать и, самое главное, *когда* их использовать.

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

Веб-разработка

Vue.JS: особенности, преимущества и недостатки

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

Введение

Реализация web UI сталкивается со все более сложными задачами, требующими использования все более сложных инструментов. Добавим сюда тренд ухода от MVC архитектуры приложений и получим довольно интересную тенденцию. Современные разработчики стремятся использовать отдельные библиотеки под каждый слой задач. На фоне этих изменений появился и стал завоевывать популярность фреймворк Vue.JS.

Чисто теоретически он является альтернативой jQuery. Но в реальности Vue.JS довольно успешно конкурирует с React.JS — очевидным лидером в области View. Из современных популярных технологий, решающих схожие задачи можно также выделить Angular и Ember. У каждой из них есть свои преимущества и недостатки. Однако все эти фреймворки можно привести к одному общему знаменателю — относительная сложность освоения.

Особенности Vue.JS

Vue.JS создавался с оглядкой на лучшие практики перечисленных технологий. Из React.JS команда Vue позаимствовала идею виртуального DOM. Этот подход исключает прямое взаимодействие с узлами интерфейса. Изначальная работа ведется с его легковесной копией (virtual DOM). И только после этого изменения применяются к реальным узлам интерфейса. Параллельно происходит сравнение реального DOM дерева и его виртуальной копии. Таким образом выявляется разница и перерисовывается только то, что претерпело изменения.

Из Angular Vue.JS позаимствовал two-way data binding. Это позволяет проектировать интерфейсы: во-первых, декларативно; во-вторых, с использованием Vue в шаблонизаторах. Таких как Haml или Pug. Правда, отметим, что такой подход практиковался и раньше. Например, во фреймворке Knockout.JS.

Ядро Vue.JS, подобно React, содержит лишь необходимый функционал для работы с интерфейсом. Поэтому оно компактно, легко интегрируется с другими технологиями, в том числе с jQuery и даже может использоваться вместо него (для разработки простых интерфейсов).

Кроме того, Vue доступен ряд подключаемых модулей, реализующих современный подход к разработке веб-приложений. О чем речь? К примеру, практически все React приложения проектируются в тандеме с технологией контроля состояний Redux, которая является отдельной библиотекой и реализует flux-архитектуру. Подход, практикуемый библиотекой Redux оказался довольно удобным и успешным. Поэтому для Vue.JS была разработана своя технология контроля состояния приложения — Vuex. Vuex полностью заимствует идеи Redux, но степень интеграции этой библиотеки с Vue гораздо выше, чем в случае React и Redux. А это трансформируется в быстродействие и удобство.

Не стоит забывать и о том, что успешность технологии напрямую зависит от активности ее пользователей. То есть — сообщества разработчиков. Так React, являясь, пожалуй, самой популярной технологией, имеет огромное количество расширений основного функционала. Они создаются участниками сообщества, и у React разработчиков имеется инструментарий на все случаи жизни. У Vue.JS в этом плане дела не так хороши. Во всяком случае — пока. Впрочем, эта технология набирает обороты и уже имеет множество полезных библиотек, решающих большинство насущных проблем разработчиков.

Опыт использования на реальном проекте

Перед нами стояла задача разработки интерфейса, содержащего на одной странице карту, фильтры, результаты фильтрации и ряд элементов взаимодействия. Мы прекрасно понимали, что использование простого Vanilla JavaScript или jQuery может обернуться сложностями в разработке. Почему не React.js? Потому что Vue.JS проще и легковеснее. Зачем связываться с более “тяжелым” фреймворком для разработки одного-единственного интерфейса?

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

Вот небольшой пример инициализации Vue приложения.

В app.js создается переменная app как Vue объект. Конструктор проходится по шаблону template.haml и производит необходимые связывания.

Декларативность в Vue настолько проста, что это позволило нам интегрировать его в монолитное веб-приложение, использующее шаблонизатор Haml.

Здесь в зависимости от правдивости флага ‘filtersOpen’ к элементу добавляется или удаляется класс ‘rentbuy__filters—open’

Vue.JS помимо того, что реализует все современные подходы в разработке web UI, является невероятно легким в освоении, гибким и высоко интегрируемым со сторонними технологиями фреймворком. Шаблонизация Vue дала нам возможность связать элементы полей фильтра с полями одного-единственного объекта. А это позволило всегда иметь под рукой актуальные опции выбора, облегчило валидацию и очистку фильтра.

Здесь значения полей ввода привязываются к полям объекта ‘data’ с помощью двустороннего связывания. Это приводит к изменению поля объекта при изменении значения в поле ввода и наоборот.

Благодаря реактивности Vue.JS, мы избавились от дублирования кода. Как это произошло? В целом ряде случаев набор полей фильтра частично совпадал (в зависимости от состояния приложения). Возникла реальная угроза массового дублирования кода. Однако с помощью простых предикатов, определенных в шаблоне, можно быстро и удобно переключаться между наборами полей.

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

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

Элементы из массива ‘properties’ рендерятся в виде Vue компонента ‘proposal’. Набор элементов на странице изменяется автоматически, согласно изменениям в массиве. Компонент ‘proposal’ представляет собой обыкновенный HTML шаблон, код которого заключен в тег script с типом ‘text/x-template’

Еще отметим, что Vue отслеживает жизненный цикл компонентов. То есть к ним можно применять какие-либо методы (изменение, удаление и т.д.) после инициализации и присоединения к DOM дереву. В частности, мы можем легко инициализировать приложение при загрузке страницы в браузере. Например, запросить начальную выборку элементов, в зависимости от текущего размера карты на странице пользователя.

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

Преимущества Vue.JS

Изучив ключевые особенности технологии и опробовав их на практике, мы можем перечислить основные преимущества фреймворка Vue:

  • Библиотека довольно проста и функциональна. Для того чтобы разобраться в ней, нужен минимальный багаж знаний.
  • Требования к стеку отсутствуют, поэтому Vue.JS можно использовать на любом проекте.
  • Фреймворк совсем немного весит. Мы экономим время загрузки страниц и получаем целую кучу плюшек: плюс к конверсии, плюс к UX, плюс к поисковому ранжированию и т.д.
  • Довольно высокая скорость разработки. Благодаря использованию любых шаблонов и доступности документации, большинство возникающих проблем решаются довольно быстро. В том числе по сравнению с React, так как в большинстве приложений, не имеющих сложных интерфейсов, вся мощь этого фреймворка является немного избыточной.
  • Возможность найти и подключить к проекту практически любого разработчика, кто хоть немного знаком с фронтенд разработкой. Низкий порог вхождения позволяет работать с фреймворком, как фронтендщикам, так и бэкэндщикам.

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

Недостатки Vue.JS

Есть и минусы. В частности, по сравнению с React.JS.

  • Работа над состоянием приложения происходит “под капотом”. В масштабных проектах это может вылиться в неочевидность работы компонентов, что зачастую создает проблемы с отладкой и эффективной разработкой.
  • Компонентный подход во Vue.JS не так гибок и очевиден, как в React.
  • Система рендеринга React более функциональна. Она предоставляет больше возможностей для отладки.
  • Шаблонизация React значительно гибче (JS vs DSL).

Вывод

Vue.JS реализует все современные подходы к разработке web UI и является легким в освоении, гибким и высоко интегрируемым со сторонними технологиями фреймворком. Безусловно, в плане создания сложных интерфейсов (например, для корпоративных сайтов) React его превосходит. Но для решения относительно простых задач использование Vue выглядит не только оправданным, но и предпочтительным.

Статью подготовил

Отдел веб-разработки

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

24.11.14 15.6K

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

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

Интернет представляет собой массу возможностей, за которые ухватываются светлые и предприимчивые умы. Конечно, веб-разработка тоже имеет свои инструменты для воплощения идей в жизнь. Один из них – язык программирования JavaScript , о котором и пойдёт речь в данной статье:

Общая информация

Многие люди, даже не имеющие никакого отношения к IT-сфере, слышали слово Java . Революционный независимый от платформ язык, на котором активно пишут приложения для мобильных систем. Он был разработан перспективной компанией Sun , которая затем перешла «под крыло » Oracle . Но ни та, ни другая компании не имеют никакого отношения к JavaScript :

От Sun потребовалось лишь разрешение на использование части названия. Удивительно, но JavaScript вообще не принадлежит ни одной фирме.

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

  • Объектно-ориентированность. Выполнение программы представляет собой взаимодействие объектов;
  • Приведение типов данных проводится автоматически;
  • Функции выступают объектами базового класса. Эта особенность делает JavaScript похожим на многие функциональные языки программирования, такие как Lisp и Haskell ;
  • Автоматическая очистка памяти. Так называемая, сборка мусора делает JavaScript похожим на C# или Java .

Если говорить о сути применения JavaScript , то этот язык позволяет «оживлять » неподвижные страницы сайтов с помощью кода, который можно запустить на исполнение (так называемые, скрипты ). То есть, можно провести аналогию с мультфильмами, где html и css – это прорисованные герои, а JavaScript – это то, что заставляет их двигаться.

Если говорить о синтаксисе JavaScript , то ему присущи следующие особенности:

  • Регистр важен. Функции с названиями func() и Func() – совершенно разные;
  • После операторов необходимо ставить точку с запятой;
  • Встроенные объекты и операции;
  • Пробелы не учитываются. Можно использовать сколько угодно отступов, а также переводов строки, чтобы оформить свой код.

Простейший код на JavaScript выглядит следующим образом:

Сфера применения

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

  • Разработка веб-приложений. Хотите установить простой счетчик, организовать передачу данных между формами или поместить на своем сайте игру? Тогда JavaScript выступит верным помощником в этом деле;
  • «Активное участие» в AJAX . Эта технология позволила значительно ускорить работу приложений, осуществляя обмен данными с сервером в «фоновом » режиме:

  • Операционные системы. Возможно, кто-то не знал, но Windows , Linux и Mac имеют своих браузерных конкурентов, львиная доля кода которых написана на JavaScript ;
  • Мобильные приложения;
  • Сфера обучения. Любая программистская специальность в университете включает в себя изучение JavaScript в том или ином объеме. Это обусловлено тем, что язык изначально разрабатывался для не очень сильных программистов. Уроки JavaScript логически вплетаются в базовый курс HTML , поэтому освоение проходит достаточно просто.

Преимущества и недостатки

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

  • Необходимость обеспечивать кроссбраузерность. Раз уж JavaScript выступает как интернет-технология, то приходится мириться с правилами, которые устанавливает всемирная паутина. Код должен корректно выполняться во всех, или хотя бы самых популярных, браузерах;
  • Система наследования в языке вызывает трудности в понимании происходящего. В JavaScript реализовано наследование, основанное на прототипах. Люди, изучавшие другие объектно-ориентированные языки программирования, привыкли к привычному «класс потомок наследует родительский класс ». Но в JavaScript такими вещами занимаются непосредственно объекты, а это не укладывается в голове;
  • Отсутствует стандартная библиотека. JavaScript не предоставляет никаких возможностей для работы с файлами, потоками ввода-вывода и прочими полезными вещами;
  • Синтаксис в целом затрудняет понимание. Красота кода – явно не конёк JavaScript , но главное правило программистов соблюдено: «Работает? Не трожь! ».

Теперь стоит отметить некоторые преимущества

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

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

Для тех, кто хочет изучать

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

  • Прежде всего, HTML . Нельзя начинать делать что-либо для интернета без основы основ. Каскадные таблицы стилей (CSS ) также очень сильно пригодятся;
  • Использовать новую литературу. Программирование – это не физика, законы которой нерушимы, а новые учебные пособия – это урезанные старые. IT-технологии постоянно развиваются, и не стоит пренебрегать полезными обновлениями;
  • Стараться самостоятельно писать все участки программы. Если что-то ну совсем не получается – можно позаимствовать чужой код, но лишь предварительно уяснив для себя каждую строчку;
  • Отладка – ваш верный друг. Быстро находить ошибки – один из важнейших моментов в программировании;
  • Не игнорируйте нормы форматирования. Конечно, код не станет лучше или хуже от разного количества отступов и пробелов, но легкость чтения и понимания программистом – тоже немаловажный момент. Код, приведенный ниже? очень трудно воспринимается, особенно если вы не его

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


Загрузка...