Метод JavaScript для переопределения поведения объекта по умолчанию при деструктурировании

#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