В этом посте мы вкратце рассмотрим предложение в стандарт ECMAScript «Record & Tuple» от Робина Рикарда и Рика Баттона. Это предложение добавляет два вида составных примитивных значений в JavaScript:  Сейчас JavaScript сравнивает по значению (то есть, просматривая содержимое) только примитивные типы данных, например, строки: Объекты же сравниваются по внутренним ссылкам (поэтому объект равен только самому себе). Предложение «Record & Tuple» от Робина Рикарда и Рика Баттона позволяет создавать составные значения, которые поддерживают сравнение по значению. Например, добавив к литералу объекта знак решётки (#), мы создадим запись — составное значение, которое сравнивается по значению и является неизменяемым: Если мы добавим знак # к литералу массива, мы создадим кортеж — массив, который сравнивается по значению и является неизменяемым: Составные значения, которые сравниваются по значению, называются составными примитивными значениями или составными примитивами. Мы можем увидеть, что записи и кортежи являются примитивами, при использовании typeof: Записи: Кортежи: Примечание: эти преобразования — поверхностные (shallow). Если какой-либо элемент (в том числе вложенный) не является примитивным, Record() и Tuple.from() бросят исключение. Примечание: эти преобразования — поверхностные (shallow). Некоторые структуры данных, такие как хеш-таблицы (hash maps) и деревья поиска (search trees), имеют слоты, в которых ключи располагаются в соответствии с их значениями. Если значение ключа изменяется, его обычно нужно поместить в другой слот. Вот почему в JavaScript значения, которые могут использоваться как ключи, либо: Составные примитивы могут быть полезны в следующих случаях: В следующих разделах мы рассмотрим эти преимущества. С составными примитивами мы можем исключить дубликаты, несмотря на то, что они не являются атомарными: Этот трюк не сработает с массивами: Так как объекты сравниваются по внутреннему идентификатору, довольно редко имеет смысл использовать их в качестве ключей объекта Map (если мы не говорим о WeakMap). Другое дело, когда мы используем составные примитивы: объект Map в строке A будет использовать записи с адресами в качестве ключа. В следующем примере мы используем метод Array.filter() (строка B), чтобы извлечь все записи, адрес которых равен адресу на строке A. Всякий раз, когда мы работаем с кэшированными данными (например, previousData в примере ниже), встроенное глубокое равенство позволяет нам эффективно проверять, изменилось ли что-нибудь. Большинство сред тестирования поддерживают глубокое сравнение для проверки, дает ли вычисление ожидаемый результат. Например, встроенный модуль Node.js assert имеет функцию deepEqual(). С составными примитивами у нас есть альтернатива такой функциональности: Примечание: учитывая, что встроенные проверки на равенство делают больше, чем просто сравнивают значения, вероятнее всего они будут поддерживать составные примитивы и будут для них более эффективны (в отличие от используемых ранее проверок глубокого равенства). Некоторыми недостатками нового синтаксиса является то, что символ # уже используется в другом месте (для приватных полей) и то, что символы, не относящиеся к буквам и цифрам всегда немного загадочны. Это можно наблюдать на следующем примере: Плюс тут в том, что этот синтаксис лаконичен. Это важно, если конструкция часто используется, а мы хотим избежать многословия. Кроме того, загадочность — намного меньшая проблема, потому что мы привыкаем к синтаксису. Вместо специального литерального синтаксиса мы могли бы использовать фабричные функции: Этот синтаксис мог бы быть улучшен, если бы JavaScript поддерживал Tagged Collection-литералы (предложение Кэт Марчан, которое она отозвала): Увы, даже если мы используем укороченные имена, результат все еще визуально загроможден: Вместо простых объектов или массивов мне нравится использовать классы для создания контейнеров с данными. Поэтому я надеюсь, что в будущем мы получим классы, экземпляры которых могут быть неизменяемыми и сравниваться по значению. Было бы также здорово, если бы у нас была поддержка глубокого и неразрушающего обновления данных, содержащих объекты, созданные такими классами. Фронтенд-разработчик