问题
I have some code which defines a getter (but no setter, if such is relevant) on a prototype. The value returned is correct in 99.99% of the cases; however, the goal is to set the property to evaluate to a different value for a specific object.
foo = {}
Object.defineProperty(foo, "bar", {
// only returns odd die sides
get: function () { return (Math.random() * 6) | 1; }
});
x = Object.create(foo);
x.bar // => eg. 5
x.bar = 4 // by fair dice roll
x.bar // nope => eg. 3
How can the property be overridden for x, an existing object, such that it is assignable (eg. has default property behavior)?
Addendum: While a new property (value or get/set) can defined on x, I am looking for if there is a way to stop the behavior of a property in the [prototype] and turn "bar" back into a normal/ad-hoc property for the specific instance.
回答1:
By using Object.defineProperty
on x
:
var foo = {}
Object.defineProperty(foo, "bar", {
// only returns odd die sides
get: function () { return (Math.random() * 6) | 1; }
});
var x = Object.create(foo);
display(x.bar); // E.g. 5
(function() {
var bar;
var proto = Object.getPrototypeOf(x); // Or just use foo
Object.defineProperty(x, "bar", {
get: function () { return typeof bar !== "undefined" ? bar : proto.bar; },
set: function(value) { bar = value; }
});
})();
display(x.bar); // Still odd
x.bar = 4; // By fair dice roll
display(x.bar); // Shows 4
function display(msg) {
document.body.insertAdjacentHTML("beforeend", "<p>" + msg + "</p>");
}
I am looking for if there is a way to stop the behavior of a property in the [prototype] and turn "bar" back into a normal/ad-hoc property.
Okay, that's slightly different, but still uses Object.defineProperty
:
var foo = {}
Object.defineProperty(foo, "bar", {
// only returns odd die sides
get: function () { return (Math.random() * 6) | 1; }
});
var x = Object.create(foo);
display(x.bar); // E.g. 5
Object.defineProperty(x, "bar", {
value: undefined,
writable: true,
enumerable: true // Or leave off if you want it non-enumerable
});
display(x.bar); // undefined
x.bar = 4; // By fair dice roll
display(x.bar); // Shows 4
function display(msg) {
document.body.insertAdjacentHTML("beforeend", "<p>" + msg + "</p>");
}
回答2:
As T.J. Crowder said, using defineProperty
again does the trick. You may consider the following variation, where the setter itself overrides the property:
Foo = function () {}
Foo.prototype = {
// computes, but only knows odd die sides
get bar() {
console.log("getter invoked")
return (Math.random() * 6) | 1
},
// fix it
set bar(value) {
console.log("setter invoked")
Object.defineProperty(
this, 'bar',
{writable: true, enumerable: true, configurable: true}
)
this.bar = value
}
}
var x = new Foo
console.log(x.bar) // => eg. 5
x.bar = 4 // by fair dice roll
console.log(x.bar) // => 4
x.bar = 2 // no setter, no getter
console.log(x.bar)
I hope you'll pardon me to rewrite in a slightly different syntax. It does not change anything to the trick. I was actually just looking for a way to override an inherited getter
when I came to this post.
来源:https://stackoverflow.com/questions/26296497/how-to-override-a-defined-get-property-on-a-prototype