web-dev-qa-db-fra.com

Puis-je étendre le proxy avec une classe ES2015?

J'ai essayé d'étendre Proxy, comme suit:

class ObservableObject extends Proxy {}

J'ai utilisé Babel pour l'envoyer à ES5, et j'ai eu cette erreur dans le navigateur:

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

J'ai regardé la ligne de code qu'il a pointée. Voici la partie du code avec des flèches pointant vers la ligne de code incriminée:

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

Est-ce que quelqu'un sait pourquoi je pourrais avoir cette erreur? Est-ce un bug de Babel? Qu'est-ce qui est censé se passer lorsque vous essayez d'étendre le proxy?

25
John L.

Non, une classe ES2015 ne peut pas étendre Proxy1.

Les objets proxy ont une sémantique très atypique et sont considérés comme des "objets exotiques" dans ES2015, ce qui signifie qu'ils "n'ont pas le comportement par défaut pour une ou plusieurs des méthodes internes essentielles devant être prises en charge par tous les objets". Ils n'ont pas de prototype, ce qui est généralement le cas pour le type que vous étendez. A partir de section 26.2.2: "Propriétés du constructeur de proxy" dans la spécification :

Le constructeur Proxy n'a pas de propriété prototype car les objets exotiques proxy ne disposent pas d'un emplacement interne [[Prototype]] nécessitant une initialisation.

Ce n'est pas une limitation de Babel. Si vous tentez d'étendre la variable Proxy dans Chrome, où elle et la syntaxe de la classe sont toutes deux prises en charge de manière native, vous obtiendrez tout de même une erreur similaire:

Uncaught TypeError: la classe étend la valeur n'a pas de propriété prototype valide undefined

1 "Non" est la réponse pratique. Cependant, Alexander O'Mara a fait remarquer que si vous attribuez une valeur à Proxy.prototype (brut!), Il devient possible d’agrandir, du moins dans certains navigateurs. Nous avons un peu expérimenté cela . En raison du comportement des instances de proxy exotiques, cela ne peut pas être utilisé pour accomplir beaucoup plus que ce que vous pourriez faire avec une fonction encapsulant le constructeur, et certains comportements ne semblent pas être cohérents entre les navigateurs (je ne suis pas sûr des attentes Si tu fais ça). S'il vous plaît, n'essayez rien de ce genre dans un code sérieux.

24
Jeremy Banks

Eh bien, j'avais oublié cette question, mais quelqu'un l'a récemment votée. Même si techniquement, vous ne pouvez pas étendre un proxy, il existe un moyen de forcer une classe à instancier en tant que proxy et à obliger toutes ses sous-classes à instancier en tant que mandataires avec les mêmes fonctions de descripteur de propriété (je l'ai seulement testé dans 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;
39
John L.

De @ John L. auto réponse:
Dans le constructeur, nous pouvons utiliser un proxy pour envelopper l’instance nouvellement créée. Pas besoin d'étendre le proxy. 

Exemple, fournissez un point observé d'une classe de points existante:

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

tester:

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
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 est une sorte de MyProxy et il a été étendu de A de classe simultanément ... Un prototype n'est pas original, il a été proxy, en quelque sorte. 

0
Weizhe Ding

Babel ne prend pas en charge le proxy, simplement parce qu'il ne le peut pas. Donc, jusqu'à ce que les navigateurs ajoutent un support, il n'existe pas.

Dans la documentation Babel: "Fonction non prise en charge En raison des limitations imposées par ES5, les procurations ne peuvent pas être transpilées ou remplies"

0
omerts
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
user3027221