Как управлять перечислением свойств (для...in) с объектами Proxy?

Я оборачиваю объект в прокси, а затем перебираю его. Как я могу управлять ключами, через которые он проходит?

Прокси работает, если я не переопределяю ключи:

var obj = {"hello": "world"}
var proxy = new Proxy(obj, {})
for (var key in proxy){
    console.log(key)
}
// logs "Hello"

Однако, если я поменяю ключи в ownKeys обработчик.

var obj = {"hello": "world"}
var proxy = new Proxy(obj, {
    ownKeys: function(){
        return ["a", "b"]
    }
})
for (var key in proxy){
    console.log(key)
}
// Logs nothing

Если я вернусь "hello" как часть ownKeys только "hello" зарегистрирован

Видимо был enumerate ловушка в ES6, но она была удалена из ES7.

Можно ли по-прежнему контролировать for...in цикл с прокси? Почему enumerate удалены из спецификации?

2 ответа

Решение

К сожалению, это невозможно сделать больше.

Как писал Брайан Терлсон (редактор спецификации EcmaScript):

проблема с прокси-перечислением trap и for-in, когда iimplementations не могут предварительно заполнить список ключей в объекте, потому что итератор вызывает наблюдаемые эффекты. Это означает, что итерация должна выполняться для каждой итерации. На прошлой встрече мы думали, что было бы хорошо, если бы перечисляемая ловушка исчерпала итератор, мы думали, что это решит проблему. Проблема заключалась в том, что в настоящее время существует заметная разница между объектом и прокси этого объекта, главным образом из-за удаления.

(Источник: https://github.com/rwaldron/tc39-notes/blob/master/es7/2016-01/2016-01-28.md#5xix-proxy-enumerate---revisit-decision-to-exhaust-iterator через https://ecmascript-daily.github.io/2016/02/10/why-remove-enumerate-and-reflect-enumerate)

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

имеет ловушку прокси

in оператор как таковой все еще может быть захвачен с помощью has ловушка прокси:

var p = new Proxy({}, {
  has: function(target, prop) {
    if (prop === 'a') { return true; }
    return false;
  }
});
'a' in p; // true
'b' in p; // false

альтернатива

Как for (let key in proxy) В наши дни циклы являются более унаследованной функцией, вы можете использовать одно из следующих с ownKeys ловушка прокси:

  • Object.keys() (только собственные перечисляемые свойства)
  • Object.getOwnPropertyNames() (собственные свойства)
  • Reflect.ownKeys() (собственные свойства и символы)

(Источник: https://twitter.com/nilssolanki/status/659839340592422912)

(но вы, вероятно, уже знали это, видя, что вы работаете с прокси в первую очередь)

user2106769 предоставил решение в качестве комментария, но для любого, как я, который не видел их комментарий, вы можете переопределить for..in итерация с ownKeys а также getOwnPropertyDescriptor:

var obj = { "hello": "world" };
var proxy = new Proxy(obj, {
    ownKeys: function() {
        return ["a", "b"];
    },
    getOwnPropertyDescriptor: function(target, key) {
         return { enumerable: true, configurable: true };
    }
});
for (var key in proxy) {
    console.log(key);
}

Предложение пользователя user2106769 и ответ yeerk о переопределении getOwnPropertyDescriptor для разрешения перечисления атрибутов Proxy имеет недостаток, о котором следует знать при его использовании, он не устанавливает атрибут value при перехвате getOwnPropertyDescriptor, таким образом, некоторый другой код, который зависит от этого поведения не будет работать правильно.

Код, демонстрирующий недостаток и его решение, приведен ниже:

var obj = { "hello": "world" };
var flawedProxy = new Proxy(obj, {
    ownKeys: function() {
        return ["a", "b"];
    },
    getOwnPropertyDescriptor: function(target, key) {
         return { enumerable: true, configurable: true };
    }
});

var goodProxy = new Proxy(obj, {
    get: function(target, key) {
      // modify something here if you want to
      return target[key];
    },
    ownKeys: function() {
        return ["a", "b"];
    },
    getOwnPropertyDescriptor: function(target, key) {
         return { value: this.get(target, key), enumerable: true, configurable: true };
    }
});

// value is accessible, getOwnPropertyDescriptor not trapped
console.log(Object.getOwnPropertyDescriptor(obj, 'hello').value);

// value is undefined, getOwnPropertyDescriptor not trapped correctly
console.log(Object.getOwnPropertyDescriptor(flawedProxy, 'hello').value);

// value is accessible, getOwnPropertyDescriptor trapped correctly
console.log(Object.getOwnPropertyDescriptor(goodProxy, 'hello').value);

Другие вопросы по тегам