¿Puedo extender el Proxy con una clase ES2015?


Traté de extender Proxy, así:

class ObservableObject extends Proxy {}

Utilicé Babel para transpilarlo a ES5, y obtuve este error en el navegador:

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

Miré la línea de código a la que apuntaba. Aquí está esa parte del código con flechas que apuntan a la línea de código ofensiva:

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);

¿Alguien sabe por qué podría estar recibiendo este error? ¿Esto es un error en Babel? ¿Qué se supone que sucede cuando intentas extender el proxy?

Author: Jeremy Banks, 2016-06-09

6 answers

No, una clase ES2015 no puede extenderse Proxy1.

Los objetos Proxy tienen una semántica muy atípica y se consideran "objetos exóticos" en ES2015, lo que significa que "no tienen el comportamiento predeterminado para uno o más de los métodos internos esenciales que deben ser compatibles con todos los objetos". No tienen ningún prototipo, que es donde normalmente obtendrías la mayor parte del comportamiento para un tipo que estás extendiendo. From section 26.2.2: "Properties of the Proxy Constructor" in the especificación:

El constructor Proxy no tiene una propiedad prototype porque los objetos exóticos proxy no tienen una ranura interna [[Prototype]] que requiera inicialización.

Esto no es una limitación de Babel. Si intenta extender Proxy en Chrome, donde tanto ella como la sintaxis de la clase son compatibles de forma nativa, obtendrá un error similar:

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

1 "No" es la respuesta práctica. Sin embargo, Alexander O'Mara señaló que si se asigna un valor a Proxy.prototype (bruto!), se hace posible extender, al menos en algunos navegadores. experimentamos con esto un poco. Debido al comportamiento de las instancias de proxy exóticas, esto no se puede usar para lograr mucho más de lo que podría hacer con una función que envuelve al constructor, y algunos comportamientos no parecen ser consistentes entre navegadores (No estoy seguro de lo que la especificación espera si hace esto). Por favor, no intentes nada como esto en serious code.

 23
Author: Jeremy Banks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:28

Bueno, me había olvidado de esta pregunta, pero alguien recientemente la votó a favor. Aunque técnicamente no puedes extender un proxy, hay una manera de forzar a una clase a instanciar como proxy y forzar a todas sus subclases a instanciar como proxies con las mismas funciones de descriptor de propiedades (solo he probado esto en 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;
 32
Author: John L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-21 08:46:13

De @John L. respuesta propia:
Dentro de constructor podemos usar Proxy para envolver la instancia recién creada. No hay necesidad de extender Proxy.

Ejemplo, proporcione un punto observado de una clase de punto existente:

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
            }
        })
    }
}

Prueba:

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)
 6
Author: Joseph Merdrignac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-05 10:16:45

Babel no soporta Proxy, simplemente porque no puede. Así que hasta que los navegadores añadan soporte, no existe.

De los documentos de Babel: "Función no compatible Debido a las limitaciones de ES5, los Proxies no pueden ser transpilados o polyfilled"

 0
Author: omerts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-09 00:01:05
class c1 {
    constructor(){
        this.__proto__  = new Proxy({}, {
            set: function (t, k, v) {
                t[k] = v;
                console.log(t, k, v);
            }
        });
    }
}

D = nuevo c1(); d. a = 123;

 0
Author: user3027221,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-16 18:15:42
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 es una especie de MyProxy y se extendió por A de clase simultáneamente. A no es el prototipo original, fue proxy, más o menos.

 0
Author: Weizhe Ding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-28 08:03:06