Javascript 装饰器:如何使用此作用域存储函数?
Javascript Decorators: How to store function with this scope?
我想创建一个装饰器,为 class 创建的每个实例将具有此作用域的函数存储在数组中
我找不到合适的词来解释所以在这里假设我有以下代码:
class Foo
{
constructor()
{
this.MyValue = "Foo";
}
@StoreFunction()
TestA()
{
console.log("Foo MyValue:", this.MyValue);
}
}
class Bar extends Foo
{
constructor()
{
this.MyValue = "Bar";
}
@StoreFunction()
TestB()
{
console.log("Bar MyValue:", this.MyValue);
}
}
function StoreFunction()
{
return function(target, key, descriptor)
{
// How would i go about saving the function there so i can call it later??
return descriptor;
}
}
我知道装饰器在 class 实例化期间不会被处理。所以我尝试在 StoreFunction.
中执行以下操作
var StoredFunctions = [];
function StoreFunction()
{
return function(target, key, descriptor)
{
if(target._StoredFunctions)
{
target._StoredFunctions = [];
}
// Save the function's name
target._StoredFunctions.push(key);
return descriptor;
}
}
然后在构造函数中绑定它们。
class Foo
{
constructor()
{
this.MyValue = "Foo";
this.BindFunctions()
}
BindFunctions()
{
if(this._StoredFunctions)
{
this._StoredFunctions.forEach( method => {
StoredFunctions.push(this[method].bind(this));
});
}
}
}
但是 this._StoredFunctions 也没有正确存储 StoredFunctions,因为它们是静态的。我不太确定如何让它正常工作。
如果问题不清楚,请告诉我我很难解释这个问题。
您可以插入一个自动子class,它可以处理 "exposing" 的必要工作(或以其他方式处理)标记的函数,请参阅评论:
// `exposable` sets the class up for `exposed` on methods
function exposable() {
return function decorator(target, name) {
// Create our subclass with the same name
const o = {[name]: class extends target {
constructor(...args) {
super(...args);
// "Expose" the bound methods
this.exposed = {};
Object.getOwnPropertyNames(target.prototype).forEach(name => {
const method = target.prototype[name];
if (method.exposed) {
this.exposed[name] = method.bind(this);
}
});
}
}};
return o[name];
};
}
// `expose` marks a method to be exposed in the constructor
function exposed(state) {
return function decorator(target, name, config) {
config.value.exposed = true;
return config;
};
}
// Example
@exposable()
class Foo {
constructor(name) {
this.name = name;
}
@exposed()
testA() {
console.log("testA says " + this.name);
}
testB() {
console.log("testB says " + this.name);
}
}
const f = new Foo("Fred");
f.exposed.testA(); // Says "testA says Fred" because it's bound
您说过我们可以依靠共同的基础 class (Foo
)。如果是这样,我们可以将逻辑移动到 Foo
本身:
// `expose` marks a method to be exposed in the constructor
function exposed(state) {
return function decorator(target, name, config) {
config.value.exposed = true;
return config;
};
}
// Example
class Foo {
constructor(name) {
this.name = name;
this.exposed = Object.create(null);
let proto = Object.getPrototypeOf(this);
while (proto && proto !== Object.prototype) {
Object.getOwnPropertyNames(proto).forEach(name => {
if (!this.exposed[name]) {
const method = this[name];
if (typeof method === "function" && method.exposed) {
// Expose it
this.exposed[name] = method.bind(this);
}
}
});
proto = Object.getPrototypeOf(proto);
}
}
@exposed()
testA() {
console.log("testA says " + this.name);
}
}
class Bar extends Foo {
@exposed()
testB() {
console.log("testB says " + this.name);
}
}
const f = new Foo("Fred");
f.exposed.testA(); // Says "testA says Fred" because it's bound
const b = new Bar("Barney");
b.exposed.testA(); // Says "testA says Barney" because it's bound
b.exposed.testB(); // Says "testB says Barney" because it's bound
T.J。 Crowder 提供了一个正确的答案,我提供了一个干净且可读的解决方案,这是我基于 T.J 提出的。克劳德.
var StoredFunctions = {};
class Foo
{
constructor()
{
this.MyValue = "Foo";
this.SetupStore();
}
SetupStore()
{
let Methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
for(let Method of Methods)
{
if(typeof(this[Method]) == "function" && this[Method]._StoredFunction)
{
StoredFunctions[Method] = this[Method].bind(this);
}
}
}
@StoreFunction()
TestA()
{
console.log("Foo MyValue:", this.MyValue);
}
}
class Bar extends Foo
{
constructor()
{
this.MyValue = "Bar";
}
@StoreFunction()
TestB()
{
console.log("Bar MyValue:", this.MyValue);
}
}
function StoreFunction(StoreInfo)
{
return function(target, key, descriptor)
{
descriptor.value._StoredFunction = true; // Mark the function to be stored on initialization
return descriptor;
}
}
我想创建一个装饰器,为 class 创建的每个实例将具有此作用域的函数存储在数组中 我找不到合适的词来解释所以在这里假设我有以下代码:
class Foo
{
constructor()
{
this.MyValue = "Foo";
}
@StoreFunction()
TestA()
{
console.log("Foo MyValue:", this.MyValue);
}
}
class Bar extends Foo
{
constructor()
{
this.MyValue = "Bar";
}
@StoreFunction()
TestB()
{
console.log("Bar MyValue:", this.MyValue);
}
}
function StoreFunction()
{
return function(target, key, descriptor)
{
// How would i go about saving the function there so i can call it later??
return descriptor;
}
}
我知道装饰器在 class 实例化期间不会被处理。所以我尝试在 StoreFunction.
中执行以下操作var StoredFunctions = [];
function StoreFunction()
{
return function(target, key, descriptor)
{
if(target._StoredFunctions)
{
target._StoredFunctions = [];
}
// Save the function's name
target._StoredFunctions.push(key);
return descriptor;
}
}
然后在构造函数中绑定它们。
class Foo
{
constructor()
{
this.MyValue = "Foo";
this.BindFunctions()
}
BindFunctions()
{
if(this._StoredFunctions)
{
this._StoredFunctions.forEach( method => {
StoredFunctions.push(this[method].bind(this));
});
}
}
}
但是 this._StoredFunctions 也没有正确存储 StoredFunctions,因为它们是静态的。我不太确定如何让它正常工作。
如果问题不清楚,请告诉我我很难解释这个问题。
您可以插入一个自动子class,它可以处理 "exposing" 的必要工作(或以其他方式处理)标记的函数,请参阅评论:
// `exposable` sets the class up for `exposed` on methods
function exposable() {
return function decorator(target, name) {
// Create our subclass with the same name
const o = {[name]: class extends target {
constructor(...args) {
super(...args);
// "Expose" the bound methods
this.exposed = {};
Object.getOwnPropertyNames(target.prototype).forEach(name => {
const method = target.prototype[name];
if (method.exposed) {
this.exposed[name] = method.bind(this);
}
});
}
}};
return o[name];
};
}
// `expose` marks a method to be exposed in the constructor
function exposed(state) {
return function decorator(target, name, config) {
config.value.exposed = true;
return config;
};
}
// Example
@exposable()
class Foo {
constructor(name) {
this.name = name;
}
@exposed()
testA() {
console.log("testA says " + this.name);
}
testB() {
console.log("testB says " + this.name);
}
}
const f = new Foo("Fred");
f.exposed.testA(); // Says "testA says Fred" because it's bound
您说过我们可以依靠共同的基础 class (Foo
)。如果是这样,我们可以将逻辑移动到 Foo
本身:
// `expose` marks a method to be exposed in the constructor
function exposed(state) {
return function decorator(target, name, config) {
config.value.exposed = true;
return config;
};
}
// Example
class Foo {
constructor(name) {
this.name = name;
this.exposed = Object.create(null);
let proto = Object.getPrototypeOf(this);
while (proto && proto !== Object.prototype) {
Object.getOwnPropertyNames(proto).forEach(name => {
if (!this.exposed[name]) {
const method = this[name];
if (typeof method === "function" && method.exposed) {
// Expose it
this.exposed[name] = method.bind(this);
}
}
});
proto = Object.getPrototypeOf(proto);
}
}
@exposed()
testA() {
console.log("testA says " + this.name);
}
}
class Bar extends Foo {
@exposed()
testB() {
console.log("testB says " + this.name);
}
}
const f = new Foo("Fred");
f.exposed.testA(); // Says "testA says Fred" because it's bound
const b = new Bar("Barney");
b.exposed.testA(); // Says "testA says Barney" because it's bound
b.exposed.testB(); // Says "testB says Barney" because it's bound
T.J。 Crowder 提供了一个正确的答案,我提供了一个干净且可读的解决方案,这是我基于 T.J 提出的。克劳德.
var StoredFunctions = {};
class Foo
{
constructor()
{
this.MyValue = "Foo";
this.SetupStore();
}
SetupStore()
{
let Methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
for(let Method of Methods)
{
if(typeof(this[Method]) == "function" && this[Method]._StoredFunction)
{
StoredFunctions[Method] = this[Method].bind(this);
}
}
}
@StoreFunction()
TestA()
{
console.log("Foo MyValue:", this.MyValue);
}
}
class Bar extends Foo
{
constructor()
{
this.MyValue = "Bar";
}
@StoreFunction()
TestB()
{
console.log("Bar MyValue:", this.MyValue);
}
}
function StoreFunction(StoreInfo)
{
return function(target, key, descriptor)
{
descriptor.value._StoredFunction = true; // Mark the function to be stored on initialization
return descriptor;
}
}