我可以使用ES2015类来扩展Proxy吗?

问题描述:

我试图扩大代理,像这样:我可以使用ES2015类来扩展Proxy吗?

class ObservableObject extends Proxy {} 

我用巴贝尔将其transpile到ES5,我得到这个错误在浏览器:

app.js:15 Uncaught TypeError: Object prototype may only be an Object or null: undefined 

我看着这行代码它指出。下面的代码与指向问题的代码行的箭部分:

var ObservableObject = exports.ObservableObject = function (_Proxy) { 
    _inherits(ObservableObject, _Proxy); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

    function ObservableObject() { 
     _classCallCheck(this, ObservableObject); 

     return _possibleConstructorReturn(this, Object.getPrototypeOf(ObservableObject).apply(this, arguments)); 
    } 

    return ObservableObject; 
}(Proxy); 

有谁知道为什么我可能会得到这个错误?这是巴别尔的一个错误吗?当您尝试扩展代理时应该发生什么?

没有,一个ES2015类不能延伸Proxy

代理对象具有非常非典型的语义,在ES2015中被认为是“异域对象”,意味着它们“不具有一个或多个必须由所有对象支持的基本内部方法的默认行为”。他们没有任何原型,这是您通常会为您正在扩展的类型获取大部分行为的原型。 From section 26.2.2: "Properties of the Proxy Constructor" in the specification

The Proxy constructor does not have a prototype property because proxy exotic objects do not have a [[Prototype]] internal slot that requires initialization.

这不是巴别塔的限制。如果试图在Chrome中,它和类的语法都原生支持扩展Proxy,你仍然会得到类似的错误:

Uncaught TypeError: Class extends value does not have valid prototype property undefined

“否”是实际的答案。但是,Alexander O'Mara指出,如果您为Proxy.prototype(总数!)指定值,则至少在某些浏览器中可以扩展。 We experimented with this a little。由于异常代理实例的行为,这不能用于完成比使用包装构造函数的函数更多的功能,并且某些行为在浏览器之间看起来并不一致(我不确定规范期望的结果如果你这样做)。请不要以严肃的代码尝试这样的事情。

+0

在一个侧面说明,修补在'prototype'属性将绕过此'TypeError'在本地的,非babel的实现,但'代理'的方式显然实现覆盖任何子类,给你留下一个令人费解的'代理'别名。 –

+0

@ AlexanderO'Mara这非常有趣。我对内部语义不够熟悉,无法理解这是明确指定的还是实现细节。我设置了'.prototype = null'并测试了一下。你的子类构造函数可以采用不同的参数,并且如果它返回一个Object,该Object将被正确地生成而不是Proxy实例,所以子类构造函数正在被正确使用。我认为我们可能成功地创建了Proxy的子类,但是由于异国情调,子类无法真正修改其实例的行为。 –

+0

我*认为*这与JavaScript构造函数返回与隐式'this'不同的对象的方式有关。如果父类构造函数执行此操作,那么当子类调用超类并继承基本上被抛出时,该对象会替换this。所以我怀疑这就是Proxy基本上在做的事情。 –

巴别尔不支持代理,只是因为它不能。因此,直到浏览器添加支持,它不存在。

从巴别文档: “不支持的特征 由于ES5的限制,代理不能transpiled或polyfilled”

class c1 { 
    constructor(){ 
     this.__proto__ = new Proxy({}, { 
      set: function (t, k, v) { 
       t[k] = v; 
       console.log(t, k, v); 
      } 
     }); 
    } 
} 

d = new c1(); d.a = 123;

+0

它可以工作,但解释了一下,请 – cske

+0

它应该被Object.setPrototypeOf代替本.__ proto__? –

嗯,我已经忘记了这个问题,但最近有人提出了这个问题。尽管你在技术上无法扩展代理,但有一种方法可以强制类实例化为代理并强制其所有子类实例化为具有相同属性描述符函数的代理(我只在Chrome中测试过):

class ExtendableProxy { 
    constructor() { 
     return new Proxy(this, { 
      set: (object, key, value, proxy) => { 
       object[key] = value; 
       console.log('PROXY SET'); 
       return true; 
      } 
     }); 
    } 
} 

class ChildProxyClass extends ExtendableProxy {} 

let myProxy = new ChildProxyClass(); 

// Should set myProxy.a to 3 and print 'PROXY SET' to the console: 
myProxy.a = 3; 
+0

我试过这个,它(当前)的作品。但是我不明白为什么,你能解释一下/为什么这会起作用吗?这似乎是巧妙地利用了一个错误,但如果是这种情况,我怀疑这不会无限期地继续工作......请在此扩展! –

+0

@Wim Barelds我不是ecma标准的专家,但是这里有一个猜测:如果你没有为子类定义构造函数,那么构造函数会变成'constructor(){super(); }'。 'super()'是对父类构造函数的调用。如果构造函数返回某个东西,那么'new ()'返回'()'返回的东西。这种行为在老式的javascript构造函数中已经有一段时间了。所以,当调用'super'时,构造的对象变成代理。 –

+1

@WimBarelds ECMAScript 6/7规范(我忘记了)指出,在类层次结构中,基类负责分配对象。因此,在层次结构中,不会继承的类(除了隐式地从Object)将负责分配。在这种情况下,ExtendableProxy将分配新的实例作为Proxy的实例。所以这个工作。我不认为这是一个错误。 –

class A { } 

class MyProxy { 
    constructor(value, handler){ 
    this.__proto__.__proto__ = new Proxy(value, handler); 
    } 
} 


let p = new MyProxy(new A(), { 
    set: (target, prop, value) => { 
    target[prop] = value; 
    return true; 
    }, 
    get: (target, prop) => { 
    return target[prop]; 
    } 
}); 

console.log("p instanceof MyProxy", p instanceof MyProxy); // true 
console.log("p instanceof A", p instanceof A); // true 

p是种MyProxy,它是由同时类的A延长。 A不是原始的原型,而是代理的,有点。

来自@John L.自我回应:
在构造函数内部,我们可以使用Proxy来包装新创建的实例。无需扩展代理。

实施例,提供从现有Point类的观测点:

class Point { 

    constructor(x, y) { 
     this.x = x 
     this.y = y 
    } 

    get length() { 
     let { x, y } = this 
     return Math.sqrt(x * x + y * y) 
    } 

} 

class ObservedPoint extends Point { 

    constructor(x, y) { 

     super(x, y) 

     return new Proxy(this, { 
      set(object, key, value, proxy) { 
       if (object[key] === value) 
        return 
       console.log('Point is modified') 
       object[key] = value 
      } 
     }) 
    } 
} 

测试:

p = new ObservedPoint(3, 4) 

console.log(p instanceof Point) // true 
console.log(p instanceof ObservedPoint) // true 

console.log(p.length) // 5 

p.x = 10 // "Point is modified" 

console.log(p.length) // 5 

p.x = 10 // nothing (skip)