Как отменить вызов Object.defineProperty? - PullRequest
34 голосов
/ 22 августа 2011

Fiddle

var Assertion = function() {
    return { "dummy": "data" };    
}

Object.defineProperty(Object.prototype, 'should', {
  set: function(){},
  get: function(){
    return new Assertion(this);
  }
});

// Insert magic here.

// This needs to be false
console.log(({}).should === undefined);

Какие варианты есть в ES5 для отмены defineProperty вызова?

Никаких глупых предложений, таких как Object.defineProperty = function() { }, пожалуйста.1010 *

Следующие Object.defineProperty(Object.prototype, 'should', {})

не работает не работает

и Object.defineProperty(Object.prototype, 'should', { value: undefined })

Бросает Uncaught TypeError: Cannot redefine property: defineProperty в V8

Object.defineProperty(Object.prototype, 'should', { 
    set: function() {},
    get: function() { return undefined; }
});

Выдает такая же ошибка

delete Object.prototype.should также не работает

1 Ответ

39 голосов
/ 22 августа 2011

В общем, вы не можете отменить вызов defineProperty, так как нет стека отмены или чего-то еще.Механизм JS не отслеживает предыдущие дескрипторы атрибутов.

Например,

Object.defineProperty(Object.prototype, 'foo', {
    configurable: true,
    value: 1,
    enumerable: false
});
Object.defineProperty(Object.prototype, 'foo', {
    get: function () {
        alert('You cannot revert me');
        return 2;
    },
    enumerable: true
});

Что вы можете сделать, это удалить или переконфигурировать атрибут или перезаписать его значение.Как упоминалось в другом ответе, флаг configurable должен быть true, если вы хотите удалить или перенастроить.Как только свойство определено с помощью configurable:false, вы не можете изменить флаг configurable.


Чтобы удалить атрибут (это, предположительно, то, что вы хотите сделать), используйте delete:

Object.defineProperty(Object.prototype, 'foo', {
    configurable: true, // defaults to false
    writable: false,
    value: 1
});
delete Object.prototype.foo;
console.log(Object.prototype.hasOwnProperty('foo')); // false

Чтобы переконфигурировать, снова используйте defineProperty и передайте другой дескриптор:

Object.defineProperty(Object.prototype, 'foo', {
    configurable: true,
    get: ...
    set: ...
});
Object.defineProperty(Object.prototype, 'foo', {
    value: undefined
});
console.log({}.foo); // undefined
console.log(Object.prototype.hasOwnProperty('foo')); // true

Как показано в этом примере, вы можете использовать defineProperty для переключения между аксессором (get / set) и свойства данных (value).


Для перезаписи используйте простое назначение.В этом случае вам нужно, чтобы флаг writable был true.Очевидно, что это не работает со свойствами доступа.Он даже генерирует исключение:

Object.defineProperty(Object.prototype, 'foo', {
    configurable: true,
    value: 1,
    writable: true // defaults to false
});
Object.prototype.foo = undefined;
console.log(Object.prototype.foo); // undefined
console.log(Object.prototype.hasOwnProperty('foo')); // true

Object.defineProperty(Object.prototype, 'foo', {
    get: function () {
        return 1;
    },
    writable: true // JS error!
});

Обратите внимание, что writable по умолчанию false, когда вы используете defineProperty, но true, когда вы используете простой синтаксис o.attr = val; для определения (ранеене существует) собственность.

...