代理:使用代理对象时调用目标父对象的静态方法

时间:2019-09-07 08:17:14

标签: javascript inheritance proxy static prototype

这对于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

语言是否允许这种功能?

1 个答案:

答案 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原型对象。