Вы здесь: Главная » JavaScript » Малоизвестные особенности JavaScript

Малоизвестные особенности JavaScript

Данная статья является переводом 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).

void-undefined

Зачем создавать специальное ключевое слово, которое возвращает undefined вместо того, чтобы просто вернуть undefined ?
Как-то это слишком заморочено, не так ли?

🚩 Интересный факт

Что ж, получается, что до ES5 вы могли назначить новое значение оригинальному undefined как undefined = "abc" в большинстве браузеров.
Отсюда и определение неопределенного undefined !
В те дни использование void было единственным способом гарантировать, что вы всегда возвращаете именно оригинал undefined.

Скобки конструктора являются необязательными

Да, скобки, которые мы добавляем после имени класса при вызове конструктора - совершенно необязательны! 😮 (при условии, что вам не нужно передавать аргументы конструктору)

Оба приведенных ниже стиля кода считаются допустимым синтаксисом JS и дадут вам одинаковые результаты!

brackets for js constructor

Конструктор со скобками и без

Скобки IIFE можно пропустить

Синтаксис для IIFE (Immediately Invoked Functional Expression - немедленно вызываемая функция) всегда был немного странным для меня.
Зачем нужны все эти скобки?

Получается, что эти дополнительные скобки нужны только для того, чтобы сообщить парсеру JavaScript, что следующий код - это функциональное выражение (Functional Expression), а не функция (Function). Зная об этом, можно представить, что есть много способов пропустить эти лишние скобки и при этом создать валидный IIFE.

function void

IIFE (без ключевого слова return)

Оператор void сообщает парсеру, что код является функциональным выражением (Functional Expression). Следовательно, мы можем пропустить скобки вокруг определения функции. И угадайте, зачем нам это? Мы можем использовать любые унарные операторы (void, +,!, - и т. д.) -  и это будет работать!

Это так круто!

Однако, если вы увлеченный наблюдатель, вы можете задаться вопросом: "Не повлияет ли унарный оператор на результат, возвращаемый IIFE?"

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

iife with return

IIFE c ключевым словом return

Мы добавляем эти скобки только для лучшей читаемости кода.

Для более глубокого погружения в IIFE стоит прочитать отличную статью Чандры Гундамараджу.

Конструкция with

Знаете ли вы, JavaScript имеет блок с оператором with? with на самом деле является ключевым словом в JS. Синтаксис для записи блока с with  выглядит следующим образом:

with добавляет все свойства переданного «объекта» в цепочку областей действия, используемую для оценки операторов.

with-statement

Пример кода с with

🚩 Интересный факт

Блок с with звучит довольно круто, правда? Это даже лучше, чем разрушение объекта.
Ну не совсем.
Использование оператора with, как правило, не рекомендуется, так как оно устарело. Это абсолютно запрещено в строгом режиме (strict mode). Оказывается, с блоками добавляются некоторые проблемы с производительностью и безопасностью в языке. Вот досада!

Конструктор функций

Ключевое слово function - не единственный способ определить новую функцию. Вы можете определить свою функцию динамически, используя конструктор Function () вместе с оператором new.

function-constructor

Создание функции с помощью конструктора new Function

Последний параметр конструктора - это строковый код функции и других параметров, перед которыми находятся аргументы функции.

🚩 Интересный факт

Конструктор функций (new Function()) является матерью всех конструкторов в JavaScript. Даже конструктор объекта является конструктором функции (!). И собственный конструктор Function также является самой Function. Следовательно, callobject.constructor.constructor ... достаточное количество раз в конечном итоге вернет конструктор Function для любого объекта в JavaScript.

Свойства функции

Мы все знаем, что функции являются объектами в JavaScript. Следовательно, никто не мешает нам добавлять пользовательские свойства в функцию. Это совершенно валидно с точки зрения JS. Тем не менее, такой подход редко используется.

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

1. Настраиваемые функции (Configurable Functions)

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

greet function with locale property

Функция greet со свойством locale

Не правда ли круто? И не нужны дополнительные переменные!

2. Функция со статическими переменными

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

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

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

generateNumber function with counter property

Функция generateNumber со свойством counter

Посмотрите на вывод значений сами:

Вы еще не устали? Впереди еще много интересного. Мы всего на полпути.

Свойства псевдомассива arguments

Я уверен, что большинство из вас знает об объекте arguments внутри функции. Это объект типа массива, доступный во всех функциях. У него есть список аргументов, переданных функции при ее вызове. Но у него также есть некоторые другие интересные свойства:

  • arguments.callee -  ссылка на вызываемую в данный момент функцию;
  • arguments.callee.caller -  ссылка на функцию, которая вызвала текущую функцию.
arguments-function

callee & caller

Примечание. Хотя ES5 запрещает использование callee & caller в строгом режиме, они все еще часто встречается во многих скомпилированных библиотеках. Так что их стоит изучать.

Литералы теговых шаблонов ( Tagged Template Literals)

Если бы вы не жили под камнем, вы бы услышали о литералах шаблона (Template literals). Шаблонные литералы - одно из многих интересных дополнений ES6. А вот знаете ли вы о литералах  теговых шаблонов (Tagged Template)?

Template literals

Template literals

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

В следующем примере наш пользовательский тег - highlight - интерпретирует значения литерала шаблона, а также заключает интерпретированные значения в строку результата в элемент <mark> для выделения.

highlight tagged template literal

Литерал тегового шаблона highlight

Многие библиотеки нашли интересные варианты использования этой функции.Ниже приведены несколько интересных примеров:
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 Getters & Setters

ES5 Getters & Setters

Геттеры и сеттеры не являются новым дополнением ES5; они всегда были там. ES5 просто добавляет удобный синтаксис к существующей функции. Чтобы узнать больше о Getters и Setters, обратитесь к этой хорошей статье.

Colours, популярная библиотека для node.js, является отличным примером использования Getters. Библиотека расширяет класс String и добавляет к нему несколько методов Getter. Это позволяет нам преобразовывать любую строку в ее цветную версию для простого использования логов, с помощью простого доступа к ее свойствам.

Оператор "запятая" (Comma operator)

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

В таком виде все выражения будут оценены, и переменной result будет присвоено значение, возвращаемое выражением N.

Возможно, вы уже использовали оператор запятой в цикле for:

Иногда это помогает при написании нескольких операторов в одной строке

...  или написание коротких стрелочных функций

 Оператор "плюс" (+)

Признайтесь, вы ведь всегда хотели быстро преобразовать строку в число? Тогда вам нужно просто добавить перед строкой оператор +.
Оператор "плюс" также работает для отрицательных, восьмеричных, шестнадцатеричных и экспоненциальных значений. Более того, он даже преобразует объект Date или Moment.js во временную метку (timestamp)!

Plus operator

Plus operator

Оператор Bang Bang (!!)

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

Но Bang Bang звучит так круто! Bang Bang или Double Bang - это хитрый трюк для преобразования любого выражения в логическое значение. Если выражение является истинным значением, оно возвращает true; в противном случае возвращается false.

Оператор Bang Bang

Оператор Bang Bang

Оператор тильды ~

Посмотрим правде в глаза - никто не заботится о побитовых операторах. Хотя когда-нибудь мы будем это использовать!

Тем не менее есть ежедневный сценарий, в котором стоит использовать оператор тильды, или Bitwise NOT.

Оказывается, при использовании с числом, оператор тильды эффективно выполняет ~ N => - (N + 1). Это выражение оценивается как 0 только тогда, когда N == -1.

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

indexOf with Tilde operator

indexOf c оператором тильда

Примечание: ES6 и ES7 добавили новый метод .include () в классы String и Array соответственно. Определенно, это более чистый способ, чем оператор тильды, чтобы проверить, существует ли элемент в массиве или строке.

Метки для конструкций

В JavaScript есть концепция операторов меток. Это позволяет нам давать названия (метки) циклам и блокам в JavaScript. Затем мы можем использовать эти метки, чтобы позже вернуться к коду при использовании break или continue.

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

labelled statements

Метки в циклах и блоках

Примечание. В отличие от некоторых других языков, JavaScript не имеет конструкции goto. Следовательно, мы можем использовать только метки с break и continue.

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

Я ❤️ JavaScript и люблю писать статьи об этом. Но они требуют много времени и усилий. Если вам понравилась эта статья, пожалуйста, поделитесь и порекомендуйте ее.

Удачного кодирования!

Источник - Little known features of JavaScript

1 Комментарий

  1. Отличные примеры

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

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