Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I extend Proxy with an ES2015 class?

I tried to extend Proxy, like so:

class ObservableObject extends Proxy {} 

I used Babel to transpile it to ES5, and I got this error in the browser:

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

I looked at the line of code it pointed to. Here's that portion of the code with arrows pointing to the offending line of code:

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

Does anyone know why I might be getting this error? Is this a bug in Babel? What is supposed to happen when you try to extend Proxy?

like image 295
John L. Avatar asked Jun 08 '16 23:06

John L.


People also ask

What are ES6 proxies?

ES6 proxies sit between your code and an object. A proxy allows you to perform meta-programming operations such as intercepting a call to inspect or change an object's property. The following terminology is used in relation to ES6 proxies: target. The original object the proxy will virtualize.

What is proxy in array?

Description. The Proxy object allows you to create an object that can be used in place of the original object, but which may redefine fundamental Object operations like getting, setting, and defining properties. Proxy objects are commonly used to log property accesses, validate, format, or sanitize inputs, and so on.


2 Answers

Well, I had forgotten about this question, but someone recently upvoted it. Even though you technically can't extend a proxy, there is a way to force a class to instantiate as a proxy and force all its subclasses to instantiate as proxies with the same property descriptor functions (I've only tested this in 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; 
like image 128
John L. Avatar answered Sep 29 '22 18:09

John L.


No, an ES2015 class cannot extend Proxy1.

Proxy objects have very atypical semantics and are considered "exotic objects" in ES2015, meaning that they do "not have the default behaviour for one or more of the essential internal methods that must be supported by all objects". They do not have any prototype, which is where you'd normally get most of the behaviour for a type you're extending. 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.

This is not a limitation of Babel. If you attempt to extend Proxy in Chrome, where it and the class syntax are both natively supported, you'll still get a similar error:

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

1 "No" is the practical answer. However, Alexander O'Mara pointed out that if you assign a value to Proxy.prototype (gross!), it does become possible to extend, at least in some browsers. We experimented with this a little. Due to the behaviour of exotic Proxy instances this can't be used to accomplish much more than you could do with a function wrapping the constructor, and some behaviour does not appear to be consistent between browsers (I'm not sure what the specification expects if you do this). Please don't attempt anything like this in serious code.

like image 28
Jeremy Avatar answered Sep 29 '22 19:09

Jeremy