【问题标题】:Proxies: Calling static methods of target's parent when using a proxy object代理:使用代理对象时调用目标父级的静态方法
【发布时间】:2019-09-07 08:17:14
【问题描述】:

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

语言是否允许这种功能?

【问题讨论】:

    标签: javascript inheritance proxy static prototype


    【解决方案1】:

    您永远不能以您的方式调用ModelgetTable(),因为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 页面Inheritance with the prototype chain 所述:

    遵循 ECMAScript 标准,someObject.[[Prototype]] 表示 someObject 的原型。自 ECMAScript 2015 起,使用访问器 Object.getPrototypeOf()Object.setPrototypeOf() 访问 [[Prototype]]。这相当于 JavaScript 属性 __proto__,它是非标准的,但实际上已被许多浏览器实现。

    它不应与函数的func.prototype 属性混淆,它指定[[Prototype]] 在用作构造函数时分配给由给定函数创建的所有对象实例。 Object.prototype 属性表示Object 原型对象。

    【讨论】:

      猜你喜欢
      • 2020-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-12
      • 2016-05-26
      • 2018-12-22
      • 1970-01-01
      相关资源
      最近更新 更多