Полезные советы по написанию более лаконичного и производительного JavaScript

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

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

Хотя многие из этих приемов удобны в любом контексте, некоторые из них могут быть лучше подходят для игры в гольф, чем код производственного уровня, где ясность часто важнее лаконичности; Я позволю тебе судить об этом!

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

1. Фильтр уникальных значений

МАССИВЫ

Тип объекта Set был введен в ES6, и вместе с ..., оператором «распространения», мы можем использовать его для создания нового массива с только уникальными значениями.

const array = [1, 1, 2, 3, 5, 5, 1]
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]

До ES6 для выделения уникальных значений требовалось гораздо больше кода!

Этот прием работает с массивами, содержащими примитивные типы: undefined, null, boolean, string и number. (Если бы у вас был массив, содержащий объекты, функции или дополнительные массивы, вам понадобился бы другой подход!)

2. Оценка короткого замыкания

УСЛОВНЫЕ ОБЯЗАТЕЛЬСТВА

Тернарный оператор - это быстрый способ написать простые (а иногда и не очень простые) условные операторы, например следующие:

x > 100 ? 'Above 100' : 'Below 100';
x > 100 ? (x > 200 ? 'Above 200' : 'Between 100-200') : 'Below 100';

Но иногда даже тернарный оператор оказывается сложнее, чем необходимо. Вместо этого мы можем использовать логические операторы ‘and’ && и ‘or’ || для еще более краткой оценки определенных выражений. Это часто называют «коротким замыканием» или «оценкой короткого замыкания».

Как это работает

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

Использование && вернет первое false или "ложное" значение. Если каждый операнд оценивается как true, будет возвращено последнее вычисленное выражение.

let one = 1, two = 2, three = 3;
console.log(one && two && three); // Result: 3
console.log(0 && null); // Result: 0

Использование || вернет первое true или «истинное» значение. Если каждый операнд оценивается как false, будет возвращено последнее вычисленное выражение.

let one = 1, two = 2, three = 3;
console.log(one || two || three); // Result: 1
console.log(0 || null); // Result: null

Пример 1

Допустим, мы хотим вернуть length переменной, но нам неизвестен тип переменной.

Мы могли бы использовать оператор if/else, чтобы проверить, является ли foo допустимым типом, но это может оказаться довольно длинным. Оценка короткого замыкания позволяет нам сделать это вместо этого:

return (foo || []).length;

Если переменная foo истинна, она будет возвращена. В противном случае будет возвращено length пустого массива: 0.

Пример 2

Были ли у вас проблемы с доступом к свойству вложенного объекта? Вы можете не знать, существует ли объект или одно из вспомогательных свойств, и это может вызвать неприятные ошибки.

Допустим, мы хотели получить доступ к свойству с именем data в this.state, но data не определено, пока наша программа не вернет запрос на выборку.

В зависимости от того, где мы его используем, вызов this.state.data может помешать запуску нашего приложения. Чтобы обойти это, мы могли бы обернуть это условным выражением:

if (this.state.data) {
  return this.state.data;
} else {
  return 'Fetching Data';
}

Но это кажется довольно повторяющимся. Оператор «или» дает более краткое решение:

return (this.state.data || 'Fetching Data');

Мы не можем реорганизовать приведенный выше код для использования &&. Оператор 'Fetching Data' && this.state.data вернет this.state.data независимо от того, undefined это или нет. Это потому, что 'Fetching Data' является «правдивым», и поэтому && всегда будет обходить его, когда он указан первым.

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

В настоящее время предлагается разрешить «необязательную цепочку» при попытке вернуть свойство глубоко в древовидной структуре. Согласно предложению, знак вопроса ? может использоваться для извлечения свойства только, если оно не null.

Например, мы могли бы преобразовать наш пример выше до this.state.data?.(), таким образом возвращая data, только если он не null.

Или, если бы мы были в основном озабочены определением state или нет, мы могли бы вернуть this.state?.data.

Предложение в настоящее время находится на этапе 1 в качестве экспериментальной функции. Вы можете прочитать об этом здесь, а теперь можете использовать в своем JavaScript через Babel, добавив @ babel / plugin-scheme-optional-chaining в ваш .babelrc файл.

3. Преобразовать в логическое значение

ПРЕОБРАЗОВАНИЕ ТИПА

Помимо обычных логических значений true и false, JavaScript также обрабатывает все остальные значения как «правдивые» или «ложные».

Если не указано иное, все значения в JavaScript являются «правдивыми», за исключением 0, "", null, undefined, NaN и, конечно же, false, которые являются «ложными».

Мы можем легко переключаться между истиной и ложью, используя отрицательный оператор !, который также преобразует тип в "boolean".

const isTrue  = !0;
const isFalse = !1;
const alsoFalse = !!0;
console.log(isTrue); // Result: true
console.log(typeof true); // Result: "boolean"

Такое преобразование типов может быть удобно в условных операторах, хотя единственная причина, по которой вы решили определить false как !1, - это игра в кодовый гольф!

4. Преобразовать в строку

ПРЕОБРАЗОВАНИЕ ТИПА

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

const val = 1 + "";
console.log(val); // Result: "1"
console.log(typeof val); // Result: "string"

5. Преобразовать в число

ПРЕОБРАЗОВАНИЕ ТИПА

Обратного можно быстро добиться с помощью оператора сложения +.

let int = "15";
int = +int;
console.log(int); // Result: 15
console.log(typeof int); Result: "number"

Это также можно использовать для преобразования логических значений в числа, как показано ниже:

console.log(+true);  // Return: 1
console.log(+false); // Return: 0

Могут быть контексты, в которых + будет интерпретироваться как оператор конкатенации, а не как оператор сложения. Когда это произойдет (и вы хотите вернуть целое число, а не число с плавающей запятой), вы можете вместо этого использовать две тильды: ~~.

Тильда, известная как «побитовый оператор НЕ», эквивалентна оператору-n — 1. Так, например, ~15 равно -16.

Использование двух тильд подряд фактически отменяет операцию, потому что — ( — n — 1) — 1 = n + 1 — 1 = n. Другими словами, ~ — 16 равно 15.

const int = ~~"15"
console.log(int); // Result: 15
console.log(typeof int); Result: "number"

Хотя я не могу придумать много вариантов использования, побитовый оператор НЕ может также использоваться с логическими значениями: ~true = -2 и ~false = -1.

6. Быстрые силы

ОПЕРАЦИИ

Начиная с ES7, стало возможным использовать оператор возведения в степень ** как сокращение для степеней, что быстрее, чем запись Math.pow(2, 3). Это простой материал, но он попал в список, потому что не многие учебники были обновлены для включения этого оператора!

console.log(2 ** 3); // Result: 8

Его не следует путать с символом ^, который обычно используется для представления экспонент, но который в JavaScript является побитовым оператором XOR.

До ES7 сокращение существовало только для степеней с основанием 2, с использованием оператора побитового сдвига влево <<:

// The following expressions are equivalent:
Math.pow(2, n);
2 << (n - 1);
2**n;

Например, 2 << 3 = 16 эквивалентно 2 ** 4 = 16.

7. Быстрый переход к целому числу

ОПЕРАЦИИ / ПРЕОБРАЗОВАНИЕ ТИПА

Если вы хотите преобразовать число с плавающей запятой в целое число, вы можете использовать Math.floor(), Math.ceil() или Math.round(). Но есть также более быстрый способ усечь число с плавающей запятой до целого числа с помощью |, побитового оператора ИЛИ.

console.log(23.9 | 0);  // Result: 23
console.log(-23.9 | 0); // Result: -23

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

Если n положительно, n | 0 округляется в меньшую сторону. Если n отрицательно, он фактически округляется в большую сторону. Точнее говоря, эта операция удаляет все, что идет после десятичной точки, усекая число с плавающей запятой до целого числа.

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

Удалить последние цифры

Оператор побитового ИЛИ также можно использовать для удаления любого количества цифр из конца целого числа. Это означает, что нам не нужно использовать подобный код для преобразования между типами:

let str = "1553"; 
Number(str.substring(0, str.length - 1));

Вместо этого побитовый оператор ИЛИ позволяет нам писать:

console.log(1553 / 10   | 0)  // Result: 155
console.log(1553 / 100  | 0)  // Result: 15
console.log(1553 / 1000 | 0)  // Result: 1

8. Автоматическое связывание в классах.

КЛАССЫ

Мы можем использовать обозначение стрелок ES6 в методах класса, и при этом подразумевается привязка. Это часто позволяет сэкономить несколько строк кода в нашем конструкторе класса, и мы с радостью можем попрощаться с повторяющимися выражениями, такими как this.myMethod = this.myMethod.bind(this)!

import React, { Component } from React;
export default class App extends Compononent {
  constructor(props) {
  super(props);
  this.state = {};
  }
myMethod = () => {
    // This method is bound implicitly!
  }
render() {
    return (
      <>
        <div>
          {this.myMethod()}
        </div>
      </>
    )
  }
};

9. Усечение массива

МАССИВЫ

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

Например, если вы знаете размер исходного массива, вы можете переопределить его свойство длины, например:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array.length = 4;
console.log(array); // Result: [0, 1, 2, 3]

Это особенно лаконичное решение. Однако я обнаружил, что метод slice() выполняется еще быстрее. Если ваша главная цель - скорость, подумайте об использовании чего-то вроде этого:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array = array.slice(0, 4);
console.log(array); // Result: [0, 1, 2, 3]

10. Получите последний элемент (ы) в массиве

МАССИВЫ

Метод массива slice() может принимать отрицательные целые числа, и, если он предусмотрен, он будет принимать значения с конца массива, а не с начала.

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(array.slice(-1)); // Result: [9]
console.log(array.slice(-2)); // Result: [8, 9]
console.log(array.slice(-3)); // Result: [7, 8, 9]

11. Форматирование кода JSON.

JSON

Наконец, вы, возможно, использовали JSON.stringify раньше, но понимаете ли вы, что это также может помочь вам сделать отступ в вашем JSON?

Метод stringify() принимает два необязательных параметра: функцию replacer, которую можно использовать для фильтрации отображаемого JSON, и значение space.

Значение space принимает целое число для количества пробелов, которое вы хотите, или строку (например, '\t' для вставки табуляции), и это может значительно упростить чтение извлеченных данных JSON.

console.log(JSON.stringify({ alpha: 'A', beta: 'B' }, null, '\t'));
// Result:
// '{
//     "alpha": A,
//     "beta": B
// }'

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

Есть какие-нибудь уловки с JavaScript? Я хотел бы прочитать их в комментариях ниже!

12. [Устарело] Длина массива кэша в циклах

ПЕТЛИ

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

Спасибо нескольким комментаторам, указавшим на это. Если вы хотите узнать больше, ознакомьтесь с этим вопросом на StackOverflow.

Для тех, кто заинтересован, раньше был некоторый стимул к написанию for (let i = 0, len = array.length; i < len; i++), а не for (let i = 0; i < array.length; i++). Это уже не так!