我有一些代码可以在原型上定义一个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
如何为x,现有对象覆盖属性,使其可分配(例如,具有默认属性行为)?
附录:虽然可以在x上定义新属性(值或获取/设置),但我正在寻找是否有办法停止 [原型]中属性的行为将“bar”变回特定实例的正常/ ad-hoc属性。
答案 0 :(得分:8)
在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>");
}
我正在寻找是否有办法停止[prototype]中属性的行为并将“bar”变回普通/ ad-hoc属性。
好的,这略有不同,但仍使用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>");
}
答案 1 :(得分:1)
T.J。克劳德说,再次使用defineProperty
可以达到目的。您可能会考虑以下变体,其中设置器本身会覆盖属性:
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)
我希望您能以稍微不同的语法重写我。它不会改变任何窍门。当我写这篇文章时,我实际上只是在寻找一种方法来覆盖继承的getter
。
答案 2 :(得分:1)
在TypeScript中,我使用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);
}
}
答案 3 :(得分:1)
您可以通过定义一个 Setter 来改变 Getter 的行为。
foo
定义为新实例对象的原型Setters/Getters(访问器属性)将被复制到其原型的新创建对象中。
<块引用>bar
的值“隐藏”在 Symbol property 后面。这可以防止直接操作。
foo
的实例并覆盖 set bar()
:选项 A:
➜ 创建新的 foo
。
➜ 使用 set bar()
更改默认值。
选项 B:
➜ 使用重新定义的 foo
创建新的 set bar()
。
➜ 访问器属性 set bar()
被值属性 bar
覆盖。
选项 C:
➜ 创建新的 foo
和 Object.assign()
一个新的默认值。
➜ 这会调用 set bar()
。
It uses [[Get]] on the source and [[Set]] on the target, so it will invoke getters and setters.
// Freeze foo. Makes sure none of the Options manipulates the prototype
const foo = Object.freeze(
// Create new Obj with NO prototype
// ➜ Use {} instead of null if you need std Object methods, too
// ➜ Add setter/getter for 'bar'
Object.create(null, {
bar: {
get() {
// Return if set by setter or random
return this[Symbol.for('bar')] ?? (Math.random() * 6) | 1;
},
set(val) {
console.log(`setter 'bar' = ${val}`);
// Hide 'bar' val behind a Symbol key (non enumerable)
this[Symbol.for('bar')] = val;
}
}
})
);
console.log('Option A: Call setter (post create) =================');
const x = Object.create(foo);
console.log(x.bar); // rnd
x.bar = 4; // setter 'bar' = 4
console.log(x.bar); // 4
x.bar = null; // setter 'bar' = null
console.log(x.bar); // rnd
console.log('Option B: Overwrite Setter (on create) ==============');
// Create new foo obj, overwrite 'bar' Setter/Getter
const create = obj => Object.create(foo, Object.getOwnPropertyDescriptors(obj));
const y = create({bar: 4});
console.log(y.bar); // 4
y.bar = 5; // overwrite prop 'bar' = 4
console.log(y.bar); // 5
y.bar = null; // overwrite prop 'bar' = null
console.log(y.bar); // null (no fallback)
console.log('Option C: Obj.assign invoces Setter (on create) ====');
// create new foo Obj, assign default
// ➜ Setter gets called on new target
const createFrom = obj => Object.assign(Object.create(foo), obj);
const z = createFrom({bar: 4});
console.log(z.bar); // 4
z.bar = 6; // setter 'bar' = 6
console.log(z.bar); // 6
z.bar = null; // setter 'bar' = null
console.log(z.bar); // rnd