对于Javascripter来说,这里非常有趣的问题
在Javascript中,在使用代理获取属性时可以拦截。
此外,通过如下所示的小黑客,可以在获取类的静态属性时进行拦截:
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
语言是否允许这种功能?
以你的方式,你永远不能称Model
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
已从继承链中消失,因此您无法从User
调用其getTable()
方法。
为了实现您的目标,我建议您使用不同的解决方案,该解决方案不会直接修改原型链。
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 {}
有了这个,您可以在代理中动态包装对象,并且能够使用继承的方法以及实例化模型类。
重要提示!正如MDN页面使用原型链继承所提到的:
遵循 ECMAScript 标准,符号
someObject.[[Prototype]]
用于指定someObject
的原型。从 ECMAScript 2015 开始,可以使用访问器 Object.getPrototypeOf(( 和 Object.setPrototypeOf(( 访问[[Prototype]]
。这相当于JavaScript属性__proto__
它是非标准的,但实际上由许多浏览器实现。它不应与函数的
func.prototype
属性混淆,后者指定在用作构造函数时要分配给给定函数创建的所有对象实例的[[Prototype]]
。Object.prototype
属性表示对象原型对象。