プロトタイプでゲッターを定義するコードがいくつかあります(ただし、関連する場合はセッターはありません)。返される値は、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のプロパティをどのようにオーバーライドできますか割り当て可能になるように(たとえば、デフォルトのプロパティ動作があります)?
補遺:新しいプロパティ(値またはget/set)をxで定義できますが、[プロトタイプ]のプロパティの動作をstopして、「バー」をオンにする方法があるかどうかを探しています。 "特定のインスタンスの通常/アドホックプロパティに戻ります。
x
で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
(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>");
}
[プロトタイプ]のプロパティの動作を停止し、「バー」を通常/アドホックプロパティに戻す方法があるかどうかを探しています。
さて、それは少し異なりますが、それでも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>");
}
T.J.としてCrowder氏によると、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
をオーバーライドする方法を探していました。
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);
}
}