Record и Tuple в JavaScript

Источник: «Records and Tupples in JavaScript»
В этой статье мы рассмотрим готовящееся предложение Record / Tuple. Как их создавать, зачем их использовать и чем они отличаются от обычных объектов и массивов?

Record и tuple — это новые примитивы (предложение второго этапа), которые по замыслу разработчиков должны быть очень похожи на объекты и массивы соответственно, но отличаться от них в одном важнейшем аспекте. Это глубокая иммутабельность.

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

Глубокая иммутабельность — это ещё более важный момент. Поскольку мы гарантируем, что все вложенные структуры иммутабельны, мы можем сравнивать глубоко вложенные структуры просто с помощью === так же, как мы сравниваем примитивы. Фактически, авторы предложения называют их новыми (составными) примитивами.

Глубоко иммутабельные примитивы позволили бы упростить код, но также позволили бы JS-движкам выполнять оптимизацию при построении, манипулировании и сравнении этих примитивов.

Как создать record/tuple

Вот как создаётся record (или tuple):

const user = #{
id: 123,
name: "John Silver",
// это tuple!
likes: #['cats', 'dogs', 'hamsters']
}

Да, это просто object/array, но с добавлением #.

Обратите внимание, что их нельзя смешивать с мутабельными значениями.

const users = #[
{ name: "John" }
]

//=> TypeError: Tuple may only contain primitive values

Как работать с record/tuple

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

// доступ к свойствам
user.id //=> 123
user.likes[1] //=> "dogs"

// Работает Object.map
Object.map(#{ x: 1, y: 2 }) //=> ['x', 'y']

// Работает Spread
const newTuple = #[ ...oldTuple, 42 ]

// есть map и filter
users
.filter(u => u.name.startsWith("John"))
.map(u => u.id)

// Кортежи итерируются
for (const u of users) { console.log(u.name); }

// Они даже JSON.stringify
JSON.stringify(#{ a: #[1, 2, 3] }); // '{"a":[1,2,3]}'

// и т.д.

В целом, вы используете привычные API.

Кроме того, есть некоторые вещи, которые нельзя делать с записями и кортежами, поскольку они не мутируют.

cont user = #{name: "John"}
user.name = "Jane"
//=> TypeError: Cannot assign to read only property 'name'

// то же самое для кортежа
const tuple = #[1, 2, 3]
tuple[0] = 5
//=> TypeError: Cannot assign to read only property '0'

// мутирующие функции отсутствуют
tuple.push(4)
//=> TypeError: tuple.push is not a function

Сравнение

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

const user1 = #{
id: 1,
имя: 'John',
likes: #['cats', 'dogs']
}

const user2 = #{
id: 2,
имя: 'John',
likes: #['cats', 'dogs']
}

// да, тот же самый пользователь
user1 === user2 // true

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

Авторы дают достаточно хорошее резюме в тексте предложения.

Дополнительные материалы

Предыдущая Статья

Как хранить данные JSON в базе данных в Laravel (с примерами)

Следующая Статья

Как исправить ошибку 419 Page Expired/CSRF token mismatch