javascript 如何“覆盖”原型上定义的(get-)属性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26296497/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to "override" a defined (get-)property on a prototype?
提问by user2864740
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.
我有一些代码在原型上定义了一个 getter(但没有 setter,如果相关的话)。返回值在 99.99% 的情况下是正确的;但是,目标是将属性设置为特定对象的不同值。
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)?
如何覆盖现有对象 x 的属性,使其可分配(例如,具有默认属性行为)?
Addendum: While a new property (value or get/set) can defined on x, I am looking for if there is a way to stopthe behavior of a property in the [prototype] and turn "bar" back into a normal/ad-hoc property for the specific instance.
附录:虽然可以在 x 上定义新属性(值或获取/设置),但我正在寻找是否有办法停止[prototype] 中属性的行为并将“bar”恢复为正常/广告-hoc 特定实例的属性。
采纳答案by T.J. Crowder
By using Object.defineProperty
on x
:
通过使用Object.defineProperty
上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.
我正在寻找是否有办法停止 [prototype] 中属性的行为并将“bar”变回正常/临时属性。
Okay, that's slightly different, but still uses Object.defineProperty
:
好的,这略有不同,但仍然使用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>");
}
回答by Champignac
As T.J. Crowder said, using defineProperty
again does the trick. You may consider the following variation, where the setter itself overrides the property:
正如 TJ Crowder 所说,defineProperty
再次使用可以解决问题。您可以考虑以下变体,其中 setter 本身会覆盖该属性:
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.
我希望你能原谅我用稍微不同的语法重写。它不会改变任何技巧。getter
当我来到这篇文章时,我实际上只是在寻找一种覆盖继承的方法。
回答by Stefan Steiger
In TypeScript, I use Object.getOwnPropertyDescriptor
and Object.defineProperty
:
在打字稿中,我使用Object.getOwnPropertyDescriptor
和Object.defineProperty
:
class TestClass
{
constructor()
{
this.autoBind(this);
this.autoTrace(this);
}
private autoBind(self: any): any
{
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
if (key !== 'constructor')
{
// console.log(key);
let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
let g = desc.get != null;
let s = desc.set != null;
if (g || s)
{
if (g)
desc.get = desc.get.bind(self);
if (s)
desc.set = desc.set.bind(self);
Object.defineProperty(self.constructor.prototype, key, desc);
continue; // if it's a property, it can't be a function
} // End if (g || s)
} // End if (desc != null)
if (typeof (self[key]) === 'function')
{
let val = self[key];
self[key] = val.bind(self);
}
} // End if (key !== 'constructor' && typeof val === 'function')
} // Next key
return self;
} // End Function autoBind
private autoTrace(self: any): any
{
function getLoggableFunction_old(func, type, name)
{
return function (...args)
{
let logText = name + '(';
for (var i = 0; i < args.length; i++)
{
if (i > 0)
{
logText += ', ';
}
logText += args[i];
}
logText += ');';
console.log(type + " " + logText);
return func.apply(self, args);
};
}
function getLoggableFunction(func, type, name)
{
return function (...args)
{
let logText = name + '(';
for (var i = 0; i < args.length; i++)
{
if (i > 0)
{
logText += ', ';
}
logText += args[i];
}
logText += ')';
console.log("Pre " + type + " " + logText + "; ");
let res = func.apply(self, args);
console.log("Post " + type + " " + logText + ":", res);
return res;
};
}
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
if (key !== 'constructor')
{
// console.log(key);
let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
let g = desc.get != null;
let s = desc.set != null;
if (g || s)
{
if (g)
desc.get = getLoggableFunction(desc.get.bind(self), "Property", "get_" + key)
if (s)
desc.set = getLoggableFunction(desc.set.bind(self), "Property", "set_" + key)
Object.defineProperty(self.constructor.prototype, key, desc);
continue; // if it's a property, it can't be a function
} // End if (g || s)
} // End if (desc != null)
// if it's not a property, it can only be a function or not a function
if (typeof (self[key]) === 'function')
{
let val = self[key];
self[key] = getLoggableFunction(val.bind(self), "Function", key);
} // End if (typeof (self[key]) === 'function')
} // End if (key !== 'constructor' && typeof val === 'function')
} // Next key
return self;
} // End Function autoTrace
get bar(): boolean
{
return this._bar;
}
set bar(value: boolean)
{
this._bar = value;
}
public hello()
{
console.log("hello", "this", this);
}
public world(x, y)
{
console.log("world", "this", this);
}
}