Данная статья является переводом Little known features of JavaScript от Viral Shah.
Считается, что JavaScript - самый простой язык для начала освоения самый сложный для достижения мастерства. Сложно тут не согласиться. Это потому, что JavaScript действительно старый и действительно гибкий язык. Он полон таинственных синтаксисов и устаревших функций. Я работаю с JavaScript уже много лет и до сих пор, время от времени, я все еще сталкиваюсь с некоторыми скрытыми уловками, о существовании которых я и не знал.
Я попытался перечислить некоторые из наименее известных фич JavaScript. Хотя некоторые из этих функций недопустимы в строгом режиме (strict mode), они все равно являются совершенно корректным кодом JavaScript. Однако, обратите внимание, я не предлагаю вам начать использовать все эти фичи. Хотя они определенно крутые, существует большой шанс, что вы получите злые взгляды от своих товарищей по команде, если начнете их использовать.
Весь используемый исходный код доступен здесь. Удачного кодирования!
Обратите внимание: я не включаю в эту статью такие вещи, как подъем (hoisting), замыкание (closures), прокси (proxies), прототипическое наследование (prototypical inheritance), асинхронное ожидание (async-await), генераторы (generators) и т. д. Хотя эти функции могут быть менее понятны, они довольно хорошо известны.
Оператор void
В JavaScript есть унарный оператор void
.Возможно, вы видели, что он используется как void (0)
или void 0
.У него есть единственная цель в жизни - оценить выражение справа и вернуть значение undefined.Использование «0» - это просто соглашение.Вам не обязательно использовать «0», это может быть любое допустимое выражение, например void <expression>
, и оно по-прежнему возвращает неопределенное значение (undefined).
Зачем создавать специальное ключевое слово, которое возвращает undefined вместо того, чтобы просто вернуть undefined ?
Как-то это слишком заморочено, не так ли?🚩 Интересный факт
Что ж, получается, что до ES5 вы могли назначить новое значение оригинальному undefined как undefined = "abc" в большинстве браузеров.
Отсюда и определение неопределенного undefined !
В те дни использованиеvoid
было единственным способом гарантировать, что вы всегда возвращаете именно оригиналundefined
.
Скобки конструктора являются необязательными
Да, скобки, которые мы добавляем после имени класса при вызове конструктора - совершенно необязательны! 😮 (при условии, что вам не нужно передавать аргументы конструктору)
Оба приведенных ниже стиля кода считаются допустимым синтаксисом JS и дадут вам одинаковые результаты!
Скобки IIFE можно пропустить
Синтаксис для IIFE (Immediately Invoked Functional Expression - немедленно вызываемая функция) всегда был немного странным для меня.
Зачем нужны все эти скобки?
Получается, что эти дополнительные скобки нужны только для того, чтобы сообщить парсеру JavaScript, что следующий код - это функциональное выражение (Functional Expression), а не функция (Function). Зная об этом, можно представить, что есть много способов пропустить эти лишние скобки и при этом создать валидный IIFE.
Оператор void
сообщает парсеру, что код является функциональным выражением (Functional Expression). Следовательно, мы можем пропустить скобки вокруг определения функции. И угадайте, зачем нам это? Мы можем использовать любые унарные операторы (void, +,!, - и т. д.) - и это будет работать!
Это так круто!
Однако, если вы увлеченный наблюдатель, вы можете задаться вопросом: "Не повлияет ли унарный оператор на результат, возвращаемый IIFE?"
Конечно, повлияет. Хорошая новость заключается в том, что если вы заботитесь о результате и говорите, что храните его в некоторой переменной, то вам больше не нужны дополнительные скобки. И это правда!
Мы добавляем эти скобки только для лучшей читаемости кода.
Для более глубокого погружения в IIFE стоит прочитать отличную статью Чандры Гундамараджу.
Конструкция with
Знаете ли вы, JavaScript имеет блок с оператором with? with на самом деле является ключевым словом в JS. Синтаксис для записи блока с with выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 |
with (object) statement // for multiple statements add a block with (object) { statement statement ... } |
with добавляет все свойства переданного «объекта» в цепочку областей действия, используемую для оценки операторов.
🚩 Интересный факт
Блок с with звучит довольно круто, правда? Это даже лучше, чем разрушение объекта.
Ну не совсем.
Использование оператораwith
, как правило, не рекомендуется, так как оно устарело. Это абсолютно запрещено в строгом режиме (strict mode). Оказывается, с блоками добавляются некоторые проблемы с производительностью и безопасностью в языке. Вот досада!
Конструктор функций
Ключевое слово function
- не единственный способ определить новую функцию. Вы можете определить свою функцию динамически, используя конструктор Function () вместе с оператором new.
Последний параметр конструктора - это строковый код функции и других параметров, перед которыми находятся аргументы функции.
🚩 Интересный факт
Конструктор функций (new Function()) является матерью всех конструкторов в JavaScript. Даже конструктор объекта является конструктором функции (!). И собственный конструктор Function также является самой Function. Следовательно,
callobject.constructor.constructor
... достаточное количество раз в конечном итоге вернет конструктор Function для любого объекта в JavaScript.
Свойства функции
Мы все знаем, что функции являются объектами в JavaScript. Следовательно, никто не мешает нам добавлять пользовательские свойства в функцию. Это совершенно валидно с точки зрения JS. Тем не менее, такой подход редко используется.
Посмотрим, в каких случаях мы могли бы применить это? Есть несколько хороших вариантов использования для этого. Например,
1. Настраиваемые функции (Configurable Functions)
Допустим, у нас есть функция greet
. Мы хотим, чтобы наша функция выводила разные приветственные сообщения в зависимости от локали. Кстати, локаль также должна быть настраиваемой. Мы можем где-то поддерживать глобальную переменную для локали или реализовать функцию, используя функциональные свойства, как показано ниже:
Не правда ли круто? И не нужны дополнительные переменные!
2. Функция со статическими переменными
Еще один похожий пример. Допустим, вы хотите реализовать генератор чисел, который генерирует последовательность упорядоченных чисел. Обычно вы используете Class или IIFE со статической переменной счетчика для отслеживания последнего значения. Таким образом, вы ограничиваете доступ к счетчику, а также избегаете загрязнения глобальной области видимости дополнительными переменными.
Но что, если мы хотим гибкости, чтобы читать или даже изменять счетчик и все же не загрязнять глобальное пространство?
В этом случае мы вполне можем создать класс с переменной счетчика и некоторыми дополнительными методами для его чтения. Второй вариант - мы позволим себе быть ленивыми и просто использовать изменение свойства в самой функции.
Посмотрите на вывод значений сами:
Вы еще не устали? Впереди еще много интересного. Мы всего на полпути.
Свойства псевдомассива arguments
Я уверен, что большинство из вас знает об объекте arguments
внутри функции. Это объект типа массива, доступный во всех функциях. У него есть список аргументов, переданных функции при ее вызове. Но у него также есть некоторые другие интересные свойства:
arguments.callee
- ссылка на вызываемую в данный момент функцию;arguments.callee.caller
- ссылка на функцию, которая вызвала текущую функцию.
Примечание. Хотя ES5 запрещает использование
callee
&caller
в строгом режиме, они все еще часто встречается во многих скомпилированных библиотеках. Так что их стоит изучать.
Литералы теговых шаблонов ( Tagged Template Literals)
Если бы вы не жили под камнем, вы бы услышали о литералах шаблона (Template literals). Шаблонные литералы - одно из многих интересных дополнений ES6. А вот знаете ли вы о литералах теговых шаблонов (Tagged Template)?
Литералы теговых шаблонов позволяют вам лучше контролировать синтаксический разбор литералов шаблона в строку, добавляя пользовательский тег в литералы шаблона. Тег - это просто функция парсера, которая получает массив всех строк и значений, интерпретируемых строковым шаблоном. Ожидается, что функция тега вернет последнюю строку.
В следующем примере наш пользовательский тег - highlight
- интерпретирует значения литерала шаблона, а также заключает интерпретированные значения в строку результата в элемент <mark>
для выделения.
Многие библиотеки нашли интересные варианты использования этой функции.Ниже приведены несколько интересных примеров:
• styled-components для React
• es2015-i18n-tag для перевода и интернационализации
• chalk для колоризации логов
Геттеры (Getters) и сеттеры (Setters)
В большинстве случаев объекты JavaScript просты. Допустим, если у нас есть объект user
, и мы пытаемся получить доступ к свойству age
с помощью user.age
, мы получаем значение свойства age
, если оно определено, или мы получаем неопределенное значение (undefined
), если это не так. Все просто.
Однако это не обязательно должно быть так просто. Объекты JavaScript имеют концепцию геттеров (getters) и сеттеров (setters). Вместо непосредственного возврата значения объекта мы можем написать нашу собственную функцию Getter, которая возвращает все, что мы хотим. То же самое в отношении установки значения с помощью сеттера.
Это позволяет нам иметь такие мощные понятия, как виртуальные поля (virtual fields), проверки полей (field validations), побочные эффекты (side-effects) при получении или установке настроек для поля.
Геттеры и сеттеры не являются новым дополнением ES5; они всегда были там. ES5 просто добавляет удобный синтаксис к существующей функции. Чтобы узнать больше о Getters и Setters, обратитесь к этой хорошей статье.
Colours, популярная библиотека для node.js, является отличным примером использования Getters. Библиотека расширяет класс String и добавляет к нему несколько методов Getter. Это позволяет нам преобразовывать любую строку в ее цветную версию для простого использования логов, с помощью простого доступа к ее свойствам.
Оператор "запятая" (Comma operator)
В JavaScript есть оператор "запятая". Он позволяет нам записать несколько выражений, разделенных запятой, в одну строку и вернуть результат последнего выражения.
1 2 |
// синтаксис let result = expression1, expression2,... expressionN |
В таком виде все выражения будут оценены, и переменной result
будет присвоено значение, возвращаемое выражением N.
Возможно, вы уже использовали оператор запятой в цикле for
:
1 |
for (var a = 0, b = 10; a <= 10; a++, b--) |
Иногда это помогает при написании нескольких операторов в одной строке
1 2 3 |
function getNextValue() { return counter++, console.log(counter), counter } |
... или написание коротких стрелочных функций
1 |
const getSquare = x => (console.log (x), x * x) |
Оператор "плюс" (+)
Признайтесь, вы ведь всегда хотели быстро преобразовать строку в число? Тогда вам нужно просто добавить перед строкой оператор +.
Оператор "плюс" также работает для отрицательных, восьмеричных, шестнадцатеричных и экспоненциальных значений. Более того, он даже преобразует объект Date
или Moment.js
во временную метку (timestamp)!
Оператор Bang Bang (!!)
Хорошо, технически это не отдельный оператор JavaScript. Это просто оператор отрицания JavaScript, используемый дважды.
Но Bang Bang звучит так круто! Bang Bang или Double Bang - это хитрый трюк для преобразования любого выражения в логическое значение. Если выражение является истинным значением, оно возвращает true
; в противном случае возвращается false
.
Оператор тильды ~
Посмотрим правде в глаза - никто не заботится о побитовых операторах. Хотя когда-нибудь мы будем это использовать!
Тем не менее есть ежедневный сценарий, в котором стоит использовать оператор тильды, или Bitwise NOT.
Оказывается, при использовании с числом, оператор тильды эффективно выполняет ~ N => - (N + 1)
. Это выражение оценивается как 0
только тогда, когда N == -1
.
Мы можем использовать этот подход, поместив ~ перед функцией indexOf (...)
, чтобы выполнить логическую проверку, существует ли подстрока в String или элемент в Array.
Примечание: ES6 и ES7 добавили новый метод
.include ()
в классы String и Array соответственно. Определенно, это более чистый способ, чем оператор тильды, чтобы проверить, существует ли элемент в массиве или строке.
Метки для конструкций
В JavaScript есть концепция операторов меток. Это позволяет нам давать названия (метки) циклам и блокам в JavaScript. Затем мы можем использовать эти метки, чтобы позже вернуться к коду при использовании break
или continue
.
Операторы-метки особенно удобны во вложенных циклах. Но мы также можем использовать их, чтобы просто организовать код в блоки или создать разрушаемый блок
Примечание. В отличие от некоторых других языков, JavaScript не имеет конструкции goto. Следовательно, мы можем использовать только метки с
break
иcontinue
.
Если вам известны какие-либо из таких фич JavaScript, или вы нашли интересные варианты использования этих возможностей, поделитесь своим опытом ниже. Я хотел бы услышать об этом!
Я ❤️ JavaScript и люблю писать статьи об этом. Но они требуют много времени и усилий. Если вам понравилась эта статья, пожалуйста, поделитесь и порекомендуйте ее.
Удачного кодирования!
Источник - Little known features of JavaScript
Отличные примеры