Java Script

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

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

Использование литералов для примитивных значений

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

let x = 1;
let y = true;
let z = ' ';

В приведенном выше коде устанавливается литеральное значение number, boolean и string каждой переменной. В качестве альтернативы возможно написать:

let x = new Number(1);
let y = new Boolean(true);
let z = new String('');

Можно также написать:

let x = Number(1);
let y = Boolean(true);
let z = String('');

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

Почему бы не использовать конструктор для создания переменных с примитивными значениями? Во-первых, то, что определено при помощи оператора new, будет иметь тип object даже в том случае, если значение его примитивно. Из-за этого становится сложнее сравнивать объекты. Например, если написать:

new String('foo') === new String('foo');

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

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

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

let x = 2;
console.log(typeof x);

Это также относится к другим примитивным типам данных. Нет никаких причин использовать оператор new для объявления переменных с примитивными значениями. Это только усложняет жизнь.

Использование new также замедляет работу интерпретатора JavaScript, поскольку он должен выполнить на одну операцию больше, чем действительно необходимо для того, чтобы объявить переменную с примитивным значением. Функции Number, String и Boolean полезны для преобразования типов из одного в другой. Например, есть:

let x = '2';

Следовательно, можно преобразовать строковый тип значения объявленной переменной в числовой при помощи функции Number:

let y = Number(x);

Используйте литералы для объявления объектов

Для объявления большинства объектов существуют специальные литералы. Например, объявить массив можно при помощи литерала [...]. Регулярные выражения могут быть объявлены при помощи специальных паттернов со слешами. При объявлении функции возможно использование ключевого слова function или стрелки ->.

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

Это означает, что Array(1) будет возвращать пустой массив с длиной, равной 1, и без содержимого, а Array(1, 2, 3) вернет массив, содержащий три элемента [1, 2, 3]. Как видно из приведенных примеров, объявление массива через конструктор вносит неясность.

Для определения функций существуют ключевое слово function и конструктор Function.

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

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

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

Конструктор Object просто заставляет вводить больше кода, чем при объявлении объектов через литералы, в остальном эти способы одинаковы. Это означает, что использовать конструктор для объявления объектов бессмысленно.

Автоматическое преобразование типов

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

1 == '1';

Тогда строка '1' будет преобразована в число. Предположим следующее:

1 + '1';

Тогда число 1 будет преобразовано в строку ‘1’, и данная запись вернет ‘11’. JavaScript просто предполагает здесь объединение. Такая операция называется конкатенация. Теперь предположим такую запись:

1 — ‘1’;

Она вернет 0, потому что такая запись предполагает вычитание двух чисел. А такая запись:

1 — 'a';

вернет NaN, поскольку происходит вычитание строки с нечисловым контекстом из числа. В логическом выражении переменные или внутренние значения вычисляются до их истинных или ложных значений. Значения, возвращающие false, включают в себя: 0, null, undefined, '' (пустая строка), false, NaN. Все остальные значения возвращают true. Предположим следующее:

0 || undefined || null || 1;

Результатом этой записи будет 1. Интерпретатор JavaScript определит все значения, которые возвращают false, и в итоге вернет единственное истинное значение  —  1.

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

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

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

Для проверки типа примитивных значений можно использовать оператор typeof. Однако следует обратить внимание, что null имеет тип object. Все остальные значения будут иметь соответствующий им тип.

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

Читайте также:


Перевод статьи John Au-Yeung:JavaScript Best Practices: Objects.