我可以使用ES2015课程扩展代理吗?

Joh*_* L. 32 javascript ecmascript-6 babeljs es6-proxy

我试图扩展代理,如下:

class ObservableObject extends Proxy {}
Run Code Online (Sandbox Code Playgroud)

我使用Babel将其转换为ES5,我在浏览器中收到此错误:

app.js:15 Uncaught TypeError: Object prototype may only be an Object or null: undefined
Run Code Online (Sandbox Code Playgroud)

我查看了它指向的代码行.这是代码的一部分,箭头指向有问题的代码行:

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);
Run Code Online (Sandbox Code Playgroud)

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

Joh*_* L. 48

好吧,我已经忘记了这个问题,但最近有人提出了这个问题.即使您在技术上无法扩展代理,也有一种方法可以强制类实例化为代理并强制其所有子类实例化为具有相同属性描述符函数的代理(我只在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;
Run Code Online (Sandbox Code Playgroud)

  • @WimBarelds ECMAScript 6/7规范(我忘了)声明,在类层次结构中,基类负责分配对象.因此,在层次结构中,不继承的类(除了隐式地从Object)负责分配.在这种情况下,ExtendableProxy将新实例分配为Proxy的实例.这样可行.我不认为这是一个错误. (3认同)
  • 2014 年,Brendan Eich(JavaScript 的创建者)明确确认从构造函数返回值不是 bug:“我们确实在 TC39 会议上讨论了构造函数中的 return expr;,我的感觉是我们希望保留该选项。类(大部分)就像糖一样,等等。” 然而,TC39 的一些成员(负责 JavaScript 的组织)确实认为该技术是一种反模式。截至 2019 年,@John L 的 ExtendableProxy 示例可以运行,并且似乎是创建 Proxy 子类的有效方法。(但是,如果观察键/值存储的更改是目标,那么扩展 Map 可能就不那么有争议了。) (3认同)
  • 虽然在纯JS中这完全没问题,TypeScript不支持它,并且对这个决定有一些很好的论证:https://github.com/Microsoft/TypeScript/issues/11588#issuecomment-257437554在TypeScript中你应该使用函数,而不是es6类,以返回新的代理并保持正确的返回类型声明. (2认同)
  • 链接到 Brendan Eich 来源:https://esdiscuss.org/topic/should-the-default-constructor-return-the-return-value-of-super (2认同)

小智 31

不,ES2015课程不能延长Proxy1.

代理对象具有非常非典型的语义,在ES2015中被认为是"异国情调的对象",这意味着它们"没有一个或多个必须由所有对象支持的基本内部方法的默认行为".它们没有任何原型,这是您通常可以获得所扩展类型的大部分行为的地方.从规范中的第26.2.2节:"代理构造函数的属性":

Proxy构造不具有prototype的性质,因为代理异国对象没有[[原型]]内部需要初始化插槽.

这不是巴别塔的限制.如果您尝试Proxy在Chrome中扩展,它本身支持它和类语法,您仍会收到类似的错误:

未捕获的TypeError:类扩展值没有未定义的有效原型属性

1 "否"是实际答案.然而,亚历山大·奥马拉指出,如果你为Proxy.prototype(总计!)分配一个值,它至少可以扩展,至少在某些浏览器中.我们对此进行了一些实验.由于外来代理实例的行为,这不能用于完成包装构造函数的功能,并且某些行为在浏览器之间似乎不一致(我不确定规范期望什么如果你这样做).请不要在严肃的代码中尝试这样的事情.


Jos*_*nac 9

来自@John L. self response:
在构造函数中我们可以使用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
            }
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

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)
Run Code Online (Sandbox Code Playgroud)


use*_*221 5

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

d = 新的 c1(); 达 = 123;

  • 应该是 Object.setPrototypeOf 而不是 this.__proto__ 吗? (2认同)