#javascript #destructuring
#javascript #деструктурирование
Вопрос:
Есть ли в JS способ переопределить поведение объекта по умолчанию, когда он деструктурирован?
// Normally destructing lifts properties from an object
const foo = {
a: 1,
b: 2,
};
const { a, b } = foo; // a = 1, b = 2
// I would like to have a method return the properties to be
// destructured
const bar = {
toObject: () => {
return { a, b };
},
};
const { a, b } = bar; // a = undefiner, b = undefined
Я знаю, что я мог бы просто использовать const { a, b } = bar.toObject();
, но это требует, чтобы потребитель объекта знал, как работают его внутренние компоненты, и нарушает принцип наименьшего удивления.
Самое близкое, что я могу придумать к тому, что я хочу, — это toJSON
волшебный метод.
Комментарии:
1. откуда в функции берутся значения
a
иb
?2. » но для этого потребителю объекта необходимо знать, как работают его внутренние компоненты » для деструктурирования уже требуется знание того, как работают внутренние компоненты. Но только с точки зрения знания того, какие свойства существуют, чтобы знать, что разрушать. Итак, я бы опроверг ваше утверждение о том, что теперь вам требуется, чтобы объект знал, как он будет деструктурирован , что более проблематично, чем необходимость знать, что у объекта есть ключи
a
иb
.3. каков ваш фактический usecase?
4. Мой вариант использования заключается в том, что я переписываю существующий класс для использования частных переменных. На данный момент я использую
Object.freeze
метод, потому что он допускает деструктурирование (которое широко используется в моем приложении), но я бы предпочел использовать локальный метод конструктора (поскольку я не считаю, что свойства должны быть открыты). Я буду первым, кто признает, что разница незначительна. Этот вопрос был больше для моего любопытства (поскольку я ничего не смог найти в документах).
Ответ №1:
Нет. Спецификация требует, чтобы правая часть разрешалась значением, которое может быть преобразовано в объект через ToObject
, который просто возвращает сам объект, если ему передан один (т. Е. никакой специальный метод для объекта не вызывается для преобразования его во что-то другое).
Комментарии:
1. Я думаю, что OP подразумевался
toObject
как нечто, что работает аналогичноtoString
— методу, который вызывается при выполнении"some string" someObject
2. @VLAZ: Конечно. И я говорю, что единственное «преобразование», которое происходит со значением, заключается в том, что вызывается
ToObject
(внутренний алгоритм), который просто возвращает значения объекта «как есть». Он не проверяет, есть ли у объекта определенный метод, и не вызывает его.3. Да, я так и подозревал. По сути, я ищу способ переопределения
ToObject
, который JS не поддерживает. Это позор.
Ответ №2:
Если бы вы использовали деструктурирование массива, это сработало бы:
const [a, b] = {
*[Symbol.iterator]() {
yield "some"; yield "stuff";
}
};
Ответ №3:
Вы можете выполнить свою toObject
работу по назначению, украсив цель прокси-сервером, который перехватывает, ownKeys
и get
подделать объект для деструктурирования:
let withToObject = obj => new Proxy(obj, {
ownKeys(o) {
return Object.keys(o.toObject())
},
get(o, prop) {
return o.toObject()[prop]
}
});
let bar = withToObject({
aa: 11,
bb: 22,
cc: 33,
toObject() {
return {
a: this.aa,
b: this.bb
};
}
});
const {a, b} = bar;
console.log(a, b)
Конечно, это влияет не только на деструктурирование, но и на любое другое взаимодействие с объектом, например сериализацию, поэтому вы должны принять меры, чтобы заставить их тоже работать. Например, для поддержки JSON, исправьте get
следующим образом:
get(o, prop) {
if (prop === 'toJSON')
return () => o; // or o.toObject(), whatever fits better
return o.toObject()[prop]
Комментарии:
1. Я предполагаю, что это имеет много плохих побочных эффектов… тем не менее, это работает, отличная идея 🙂
2. Это работает в соответствии с моей формулировкой, но оно раскрывает свойства
a
иb
перед деструктурированием (например,bar.a === 11
)3. @thelastshadow: да, это так… деструктурирование — это просто синтаксический сахар,
{a} = x
такой же, какa = x.a