Практические советы по улучшению кода на JavaScript

26.02.2018 JS

В этой статье я приведу несколько практических советов из своего опыта как улучшить свой код на JavaScript.

Используйте TypeScript

Первое, что вы можете сделать, чтобы улучшить свой код на JS, это не писать на JS. Используйте TypeScript. Для непосвященных TypeScript (TS) – это отдельный язык программирования расширяющий стандартный JS (все, что выполняется в JS, запускается в TS).

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

TypeScript обеспечивает «безопасность типов».

Безопасность типов это такой процесс, в котором компилятор проверяет, что все типы используемые «легальным» способом в пределах компилируемого фрагмента кода. Другими словами, если вы создаете функцию foo, которая принимает число:

  • function foo(someNum: number): number {
  • return someNum + 5;
  • }

Эта функция foo должна вызываться только с числом:

хорошо

  • console.log(foo(2)); // prints «7»

плохо

  • console.log(foo(«two»)); // invalid TS code

Помимо накладных расходов на добавление типов в ваш код, нет никаких минусов в обеспечении безопасности типов. С другой стороны, выгода слишком велика, чтобы ее игнорировать. Безопасность типов обеспечивает дополнительный уровень защиты от распространенных ошибок / багов, что является благословением для таких языков, как JS.

Типы Typescript, облегчают рефакторинг крупных приложений.

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

Например, если у меня может есть функция myAPI, которая используется в 1000 различных мест:

  • function myAPI(someNum, someString) {
  • if (someNum > 0) {
  • leakCredentials();
  • } else {
  • console.log(someString);
  • }
  • }

и вдруг мне нужно немного поменять сигнатуру вызова:

  • function myAPI(someString, someNum) {
  • if (someNum > 0) {
  • leakCredentials();
  • } else {
  • console.log(someString);
  • }
  • }

Я должен быть на 100% уверен, что в каждом месте, где используется эта функция (1000 мест), я корректно обновляю использование. Если я пропущу даже одно место, мои учетные данные используемые внутри функции могут пропасть. Вот тот же сценарий с TS:

до изменения

  • function myAPITS(someNum: number, someString: string) {}

после

  • function myAPITS(someString: string, someNum: number) {}

Как видите, функция myAPITS претерпела те же изменения, что и аналог в JavaScript. В JavaScript этот код работает без ошибки в TypeScript у вас сразу будет ошибка из-за неправильного типа. И из-за «безопасности типов», которую мы обсуждали ранее, эти 1000 случаев будут блокировать компиляцию, и ваши учетные данные не будут потеряны (а это всегда хорошо).

TypeScript облегчает общение в командной архитектуре.

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

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

  • interface BasicRequest {
  • body: Buffer;
  • headers: { [header: string]: string | string[] | undefined; };
  • secret: Shhh;
  • }

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

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

Используйте современные функции

JavaScript – один из самых популярных (если не самый) языков программирования в мире. Вы можете ожидать, что 20-летний язык, которым пользуются сотни миллионов людей, к настоящему времени будет «всем понятным и известным», но на самом деле все наоборот. В последнее время в JS было сделано много изменений и дополнений. Как человек, который начал писать JS только в последние 2 года, у меня было преимущество, что я пришел без предвзятости или ожиданий. Это привело к гораздо более прагматичному, нерелигиозному выбору того, какие особенности языка использовать, а какие избегать.

async и await

Долгое время асинхронные, управляемые событиями обратные вызовы (callback) были неизбежной частью разработки JS:

традиционный callback

  • makeHttpRequest(‘google.com’, function (err, result) {
  • if (err) {
  • console.log(‘Oh boy, an error’);
  • } else {
  • console.log(result);
  • }
  • });

Я не собираюсь тратить время на объяснение того, почему вышеизложенное является проблематичным. Чтобы решить проблему с обратными вызовами, в JS была добавлена новая концепция «Promises(Обещания)». Promises позволяют вам писать асинхронную логику, избегая проблем с вложениями, которые ранее преследовали код на основе обратного вызова (callback).

Promises

  • makeHttpRequest(‘google.com’).then(function (result) {
  • console.log(result);
  • }).catch(function (err) {
  • console.log(‘Oh boy, an error’);
  • });

Самым большим преимуществом Promises по сравнению с обратными вызовами является удобочитаемость и возможность объединения в цепочки через оператор then.

Несмотря на то, что Promises великолепны, они все же оставляют желать лучшего. В конце концов, написание Promises все еще не казалось “родным” для JS. Чтобы исправить это, комитет ECMAScript решил добавить новый метод использования promises, async и await:

async и await

  • try {
  • const result = await makeHttpRequest(‘google.com’);
  • console.log(result);
  • } catch (err) {
  • console.log(‘Oh boy, an error’);
  • }

Единственное предупреждение: все что вы используете с await, должно быть объявлено с async:

функция makeHttpRequest из предыдущего примера

  • async function makeHttpRequest(url) {
  • // …
  • }

Также возможно использовать await с Promise напрямую, так как асинхронная функция – это просто необычная оболочка для Promise. Это также означает, что код async / await и код Promise функционально эквивалентны. Так что не стесняйтесь использовать async / await как можно чаще.

let и const

На протяжении большей части существования JS существовал только один способ объявления переменных var. У var есть довольно уникальные / интересные правила относительно того, как он обрабатывает область видимости.

Поведение в области видимости var противоречиво и сбивает с толку, что приводит к неожиданному поведению и, следовательно, к ошибкам в течение всего жизненного цикла JS. Но в ES6 есть альтернатива var, const и let. На данный момент не нужно больше использовать var. Любая логика, которая использует var, всегда может быть преобразована в эквивалентный const и let.

Что касается использования const против let, я всегда начинаю с объявления всех переменных с const. const намного более ограничен и «неизменяем», что обычно приводит к лучшему коду. Не существует так уж много «реальных сценариев», где необходимо использовать только let, я бы сказал, что 1/20 переменных я объявляю с помощью let остальные с const.

Стрелочные функции Arrow => Functions

Функции стрелок являются кратким методом объявления анонимных функций в JS. Анонимные функции, описывают функции, которые не имеют явного имени. Обычно анонимные функции передаются как обратный вызов или перехват событий (event hook).

обычная анонимная функция

  • someMethod(1, function () { // has no name
  • console.log(‘called’);
  • });

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

стрелочная анонимная функция

  • someMethod(1, () => { // has no name
  • console.log(‘called’);
  • });

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

В некоторых случаях функции стрелок могут быть еще более краткими:

  • const added = [0, 1, 2, 3, 4].map((item) => item + 1);
  • console.log(added) // prints «[1, 2, 3, 4, 5]»

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

Оператор Spread (распространения) ...

Извлечение пар ключ / значение одного объекта и добавление их в качестве потомков другого объекта – очень распространенный сценарий. Исторически, было несколько способов сделать это, но все эти методы довольно неуклюжи:

  • const obj1 = { dog: ‘woof’ };
  • const obj2 = { cat: ‘meow’ };
  • const merged = Object.assign({}, obj1, obj2);
  • console.log(merged) // prints { dog: ‘woof’, cat: ‘meow’ }

Этот шаблон невероятно распространен, поэтому описанный выше подход быстро становится громоздким. Благодаря «оператору Spread» его больше никогда не нужно использовать:

  • const obj1 = { dog: ‘woof’ };
  • const obj2 = { cat: ‘meow’ };
  • console.log({ …obj1, …obj2 }); // prints { dog: ‘woof’, cat: ‘meow’ }

Самое замечательное, что это также без проблем работает с массивами:

  • const arr1 = [1, 2];
  • const arr2 = [3, 4];
  • console.log([ …arr1, …arr2 ]); // prints [1, 2, 3, 4]

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

Шаблонные строки / Шаблонные литералы

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

  • const name = ‘Ryland’;
  • const helloString =
  • `Hello
  • ${name}`;

Я думаю, что код говорит сам за себя. Какая удивительная реализация.

Деструктуризация объекта

Деструктуризация объекта – это способ извлечения значений из коллекции данных (объект, массив и т. д.) без необходимости перебирать данные или явно обращаться к их ключам:

старый путь

  • function animalParty(dogSound, catSound) {}
  • const myDict = {
  • dog: ‘woof’,
  • cat: ‘meow’,
  • };
  • animalParty(myDict.dog, myDict.cat);

деструктуризация

  • function animalParty(dogSound, catSound) {}
  • const myDict = {
  • dog: ‘woof’,
  • cat: ‘meow’,
  • };
  • const { dog, cat } = myDict;
  • animalParty(dog, cat);

Но подождите, это еще не все. Вы также можете определить деструктурирование в сигнатуре функции:

деструктуризация 2

  • function animalParty({ dog, cat }) {}
  • const myDict = {
  • dog: ‘woof’,
  • cat: ‘meow’,
  • };
  • animalParty(myDict);

Это также работает с массивами:

деструктуризация 3

  • [a, b] = [10, 20];
  • console.log(a); // prints 10

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Контакты

+38 (044) 464-28-17
+38 (063) 184-13-41
+38 (097) 978-54-92
+38 (099) 274-56-60
info@valsorokin.ru
valsorokin@gmail.com

О нас

ВИДЕО В КАЧЕСТВЕ ФОНА ДЛЯ САЙТА НА HTML5+CSS