Chrome 如何撤消Object.defineProperty调用?

plicqrtu  于 2023-02-14  发布在  Go
关注(0)|答案(2)|浏览(200)

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() { }这样愚蠢的建议。
以下Object.defineProperty(Object.prototype, 'should', {})
是否not work
Object.defineProperty(Object.prototype, 'should', { value: undefined })
在V8中抛出Uncaught TypeError: Cannot redefine property: defineProperty

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

抛出same error
delete Object.prototype.should也是does not work

ux6nzvsh

ux6nzvsh1#

一般来说,你不能撤销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!
});

请注意,当您使用defineProperty时,writable默认为false,但当您使用简单语法o.attr = val;定义(以前不存在的)属性时,true默认为false

mv1qrgav

mv1qrgav2#

如果你想撤销最后一个defineProperty或者所有的defineProperty,你可以使用这个类:
(gist here

class PropertyDescriptorStack {
    private readonly descriptors: PropertyDescriptor[] = [];
    constructor(private readonly target: Object, private readonly prop: string) {
        if (!target || typeof prop !== "string") { // your choice to define ""
            throw new Error("PropertySaver: no object or property");
        }
    }

    public push(props: Partial<PropertyDescriptor>): boolean {
        this.saveDescriptor(this.target, this.prop);
        try {
            Object.defineProperty(this.target, this.prop, {
                ...props,
                configurable: true,
            });
            return true;
        }
        catch (e) {
            console.error(`Error setting property ${this.prop} on ${this.target}: ${e}`);
            return false;
        }
    }

    public pop(toStart?: boolean): boolean {
        const ind = toStart ? 0 : this.descriptors.length - 1;
        const descriptor = this.descriptors[ind];
        if (!descriptor) {
            return false;
        }
        this.descriptors.splice(ind, this.descriptors.length - ind);
        try {
            Object.defineProperty(this.target, this.prop, descriptor);
            return true;
        }
        catch (e) {
            console.error(`Error resetting property ${this.prop} on ${this.target}: ${e}`);
            return false;
        }
    }

    /**
     * Saves the current descriptor of the property in the object in the descriptors stack.
     * The descriptor is taken either from the object or from the closest prototype that has this prop.
     * If none is found, a new descriptor is generated with the current value.
     * @param target
     * @param prop 
     * @returns The found descriptor
     */
    private saveDescriptor(target: object, prop: string): PropertyDescriptor {
        let ds: PropertyDescriptor | null = null;
        for (let o: any = target, ds: PropertyDescriptor = null; o; o = Object.getPrototypeOf(o)) {
            ds = Object.getOwnPropertyDescriptor(o, prop);
            if (ds) {
                break;
            }
        }
        ds = ds || {
            configurable: true,
            writable: true,
            value: target[prop],
            enumerable: true
        }
        this.descriptors.push(ds);
        return ds;

    }
}

相关问题