Babel.js 我可以使用ES2015类扩展代理吗?

lstz6jyr  于 2023-03-06  发布在  Babel
关注(0)|答案(7)|浏览(217)

我尝试扩展代理,如下所示:

class ObservableObject extends Proxy {}

我用Babel将其传输到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);

有人知道为什么我可能会得到这个错误吗?这是巴别塔的一个bug吗?当你试图扩展代理时应该发生什么?

yb3bgrhw

yb3bgrhw1#

好吧,我忘了这个问题,但最近有人投了反对票。尽管你在技术上不能扩展代理,但有一种方法可以强制一个类示例化为代理,并强制它的所有子类示例化为代理,使用相同的属性描述符函数(我只在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;
xtupzzrd

xtupzzrd2#

不可以,ES2015类不能扩展Proxy 1。
代理对象具有非常不典型的语义,在ES2015中被认为是"外来对象",这意味着它们"没有一个或多个必须被所有对象支持的基本内部方法的默认行为"。它们没有任何原型,而这通常是你要扩展的类型的大部分行为。规范中的"代理构造函数的属性":
Proxy构造函数没有prototype属性,因为代理外来对象没有需要初始化的Prototype内部槽。
这并不是Babel的限制,如果你试图在Chrome中扩展Proxy,它和类语法都是原生支持的,你仍然会得到类似的错误:
未捕获的类型错误:类扩展值未定义有效的prototype属性
1 "不"是实际的答案,然而,Alexander O'Mara指出,如果你给Proxy.prototype赋值(真恶心!),至少在某些浏览器中,扩展. We experimented with this a little是可能的。由于奇异的代理示例的行为,这不能用来完成比使用一个函数 Package 构造函数所能完成的更多的任务。而且有些行为在浏览器之间似乎并不一致(我不确定如果你这样做的话规范会期望什么)。请不要在严肃的代码中尝试类似的事情。

ffscu2ro

ffscu2ro3#

来自@John L.自我回应:
在构造函数内部,我们可以使用代理来 Package 新创建的示例。不需要扩展代理。
例如,从现有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)
rqmkfv5c

rqmkfv5c4#

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

d =新的c1();d. a = 123;

pepwfjgg

pepwfjgg5#

Babel不支持Proxy,因为它不能,所以除非浏览器增加支持,否则它不存在。
巴别塔文献:“不支持的功能由于ES5的限制,代理无法转移或聚合填充”

3gtaxfhh

3gtaxfhh6#

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不是原始原型,它被代理了。

yws3nbqq

yws3nbqq7#

下面的代码示例显示了如何构造一个模拟类和子类的代理层次结构。它是通过用多个代理对象 Package 一个标准对象并明智地使用处理程序get选项来实现的。
代理的handlerMix-in Pattern的一种形式,我们可以使用mix-in模式模拟子类化。
该代码包含两种类型的代理“类”:EmitterBaseEmitterNet。它使用这些来收集特定于普通EventEmitter或其子类Net之一的统计信息。EmitterNet不复制EmitterBase的功能,而是通过 Package 它来重用Base。注意,在示例中,我们 Package http.Server:子类Net和EventEmitter的子类。
传递给代理的处理程序实现子类代理的所有行为。多个处理程序版本实现子类。例如,在EmitterBase中,我们收集对onemit的呼叫的统计信息EmitterBase还实现了幻像成员和方法来跟踪和报告这些计数。这相当于一个具有这些方法和成员的基类。请参见wrapEmitterBase中的handler
接下来,我们使用另一个处理程序(参见wrapEmitterNet)创建一个Proxy“子类”,它实现了两个新的phantom成员,用于对Net特定调用进行计数(listenclose)。它还实现了一个方法stats(),该方法覆盖基类中的一个方法***,并调用被覆盖的方法。***
代理标准为我们提供了足够的特性来实现代理子类化,而无需求助于类 Package 器和混乱的this

import * as util from 'node:util';
import http from 'node:http';
import { EventEmitter } from 'node:events';

async function DemoProxyHierarchy()
{
  const greeter = wrapEmitterBase(new EventEmitter());

  greeter.on("hello", (person) => console.log((`Hello, ${person}!`)));
  greeter.emit("hello", "World");
  greeter.emit("hello", "Benjamin");

  console.log(`on   calls: ${greeter.countOn}`);
  console.log(`emit calls: ${greeter.countEmit}`);
  console.log(`statistics: ${JSON.stringify(greeter.stats())}`);

  const stats = new Promise((Resolve, reject) => {
    let steps = 0;
    const server = http.createServer((req, res) => { res.end() });
    const netWrapper = wrapEmitterNet(server) as any;
    const done = () => {
      if (++steps > 2) {
        console.log(`\non   calls: ${netWrapper.countOn}`);
        console.log(`emit calls: ${netWrapper.countEmit}`);
        netWrapper.close(() => Resolve(netWrapper.stats()));
      }
    };

    netWrapper.listen(8080, done);
    http.get('http://localhost:8080', done);
    http.get('http://localhost:8080', done);
  });

  return stats.then(s => console.log(`net  stats: ${JSON.stringify(s)}`));
}

function wrapEmitterBase(ee: EventEmitter)
{
  const stats = { on: 0, emit: 0 };
  const handler = {
    get: (target, key) => {
      switch (key) {
        case "countOn":   return stats.on;
        case "countEmit": return stats.emit;
        case "stats":     return () => ({ ...stats });
        case "on":        { stats.on++;   break; }
        case "emit":      { stats.emit++; break; }
      }

      return target[key];
    },
  }

  return new Proxy(ee, handler);
}

function wrapEmitterNet(ee: EventEmitter)
{
  const stats = { listen: 0, close: 0 };
  const handler = {
    get: (target, key) => {
      switch (key) {
        case "stats":  {
          return () => ({ ...target[key](), ...stats });
        }
        case "listen": { stats.listen++; break; }
        case "close":  { stats.close++;  break; }
      }

      return target[key];
    },
  };

  return new Proxy(wrapEmitterBase(ee), handler);
}

// IIFE
(()=> { await DemoProxyHierarchy() })();

/* Output:

Hello, World!
Hello, Benjamin!
on   calls: 1
emit calls: 2
statistics: {"on":1,"emit":2}

on   calls: 1
emit calls: 5
net  stats: {"on":2,"emit":6,"listen":1,"close":1}
*/

相关问题