这对于Javascripters来说确实是一个有趣的问题
在 JavaScript 中,使用 proxies 获取属性时可以进行拦截。
还有如下所示的小技巧,可以在获取类的静态属性时进行拦截:
class Handler{
constructor(object){
this.object = object;
}
get(target, property){
if (property === 'all') {
return () => `selecting data from table: ${this.object.table}` ;
}
return target[property];
}
}
class User{
static table = 'users'
}
Object.setPrototypeOf(User, new Proxy({}, new Handler(User)));
console.log(User.all()); // prints "selecting data from table: users"
问题出现在我尝试扩展User
类,然后尝试在User
的父类下调用静态方法时:
class Handler{
constructor(object){
this.object = object;
}
get(target, property){
if (property === 'all') {
return () => `selecting data from table: ${this.object.getTable()}` ;
}
return target[property];
}
}
class Model{
static getTable(){return this.table;}
}
class User extends Model{
static table = 'users'
}
Object.setPrototypeOf(User, new Proxy({}, new Handler(User)));
console.log(User.all());
运行此命令将使您TypeError: this.object.getTable is not a function
。
调试代码后,我发现this.object
不是User
类,而是某种function
。
通过任何变通办法,是否可以调用父级的静态方法getTable
?
第二个问题,如果您设法解决第一个问题,那就是我无法再实例化User
类:
console.log(new User());
TypeError: Super constructor [object Object] of User is not a constructor
我认为这是因为User
不再是一个类:console.log(User)
导致ƒ [object Function]
而不是class User extends Model
语言是否允许这种功能?
答案 0 :(得分:2)
以您的方式,您将永远无法调用Model
的{{1}},因为getTable()
不再是Model
的父项。
让我们在此代码之后检查原型链:
User
到目前为止,原型链就像您期望的那样。
但是由于语句class Model{
static getTable(){return this.table;}
}
class User extends Model{
static table = 'users'
}
console.log(Object.getPrototypeOf(Model));
// f () { [native code] }
// 'parent' of Model is Function (class is a constructor function in JS)
// Model.[[Prototype]] === Function.prototype
console.log(Object.getPrototypeOf(User));
// class Model{
// static getTable(){return this.table;}
// }
// 'parent' of User is Model
// User.[[Prototype]] === Model.prototype
的原型链已被修改,因此继承看起来像这样:
Object.setPrototypeOf(User, new Proxy({}, new Handler(User)));
如您所见,const userPrototype = Object.getPrototypeOf(User);
console.log(userPrototype);
// Proxy {}
// 'parent' of User is an instance of Proxy({}, new Handler(User)))
const userPrototypePrototype = Object.getPrototypeOf(userPrototype);
console.log(userPrototypePrototype);
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
// 'parent' of Proxy instance is an Object instance (because target of your proxy is {})
Object.getPrototypeOf(userPrototypePrototype);
// null
// 'parent' of Object instance is null (this is the end of prototype chain)
从继承链中消失了,因此您无法从Model
调用其getTable()
方法。
为了实现您的目标,我建议您使用一种不直接修改原型链的解决方案。
User
通过此方法,您可以将对象动态包装在代理中,并且可以使用继承的方法以及实例化模型类。
重要提示! 如MDN页面Inheritance with the prototype chain所述:
遵循ECMAScript标准,符号
class Handler{ constructor(object){ this.object = object; } get(target, property){ if (property === 'all') { return () => `selecting data from table: ${this.object.getTable()}` ; } return target[property]; } } class Model{ static getTable(){return this.table;} } class User extends Model{ static table = 'users' } class ProxyClass { constructor(object) { return new Proxy(object, new Handler(object)); } } const UserProxy = new ProxyClass(User); // this is the same as // const UserProxy = new Proxy(User, new Handler(User)) console.log(UserProxy.all()); // selecting data from table: users console.log(new UserProxy()); // User {}
用于指定someObject.[[Prototype]]
的原型。自ECMAScript 2015起,使用访问器Object.getPrototypeOf()和Object.setPrototypeOf()访问someObject
。这等效于JavaScript属性[[Prototype]]
,它是非标准的,但实际上是由许多浏览器实现的。不应将其与函数的
__proto__
属性混淆,该属性指定将func.prototype
用作给定函数在用作构造函数时创建的对象的所有实例。[[Prototype]]
属性代表Object原型对象。