TypeError when assigning a finely cloned object to a method that calls super

I am testing the following code on Chrome Canary 63 and Chrome 61. The purpose of the code is to set a method that calls super on an instance of class ( z ). In my code base, sometimes blocks of property blocks are cloned from Object.assign before being added to the instance, and whenever this happens, the code crashes.

I reproduced the problem with the code example below. The code works fine if you avoid minor cloning ( props = Object.assign({}, props); ), but if I add this line, I get TypeError: (intermediate value).bar is not a function .

I tried doing Object.create(this, Object.getOwnPropertyDescriptors(props)) instead of Object.assign , but this leads to the same error.

Is there a way to correctly set super on cloned objects?

 let Moo = class { bar() { return " [bar on Moo] "; } }; let Zoo = class extends Moo { bar() { return " [bar on Zoo] " + super.bar(); } }; function addProps(inst, props) { // Code works if the line below is commented out but otherwise // results in TypeError: (intermediate value).bar is not a function props = Object.assign({}, props); // <-- Offending code Object.setPrototypeOf(props, inst.__proto__); Object.assign(inst, props); } let props = { onZooInst: { bar() { return " [bar on overridden instance] " + super.bar(); } } }; let z = new Zoo(); addProps(z, props.onZooInst); console.log(z.bar()); 
+5
source share
3 answers

For those who may run into the same problem, I finally found a solution.

Instead of defining object literal blocks such as

 let a = { someFunc() { super.someFunc(); } } 

You can replace them with an arrow function that returns them:

 let a = () => { return { someFunc() { super.someFunc(); } } }; 

this works because a().someFunc !== a().someFunc - in other words, you get a new function, and we do not need to allow the creation of dynamic code (eval / new Function). You should safely execute Object.setPrototypeOf on the return value of a() .

0
source

Is there a way to correctly set super on cloned objects?

Nope. super uses the [[HomeObject]] function slot, which is set when the function (method) is created and cannot be changed at present.

Probably the best quote from the specification on it is in Table 27 :

[[HomeObject]] : If a function uses super , this is the object whose [[GetPrototypeOf]] provides the object where the search for super properties begins.

The only thing you can do is try to change the prototype of the object referenced by the [[HomeObject]] function, but that A) Gets ugly fast and B) Kills performance (in situations where it matters).

If you do a lot of method cloning, you'd better use simple properties that relate to functions, not methods, rather than using super .

+3
source

If you interpret the requirement correctly, you can set bar to a props object on this.bar() and use Function.prototype.call() , Function.prototype.apply() or Reflect.apply() to set this function call to props.onZooInst.bar on Moo.prototype

 let Moo = class { bar() { return " [bar on Moo] "; } }; let Zoo = class extends Moo { bar() { return " [bar on Zoo] " + super.bar(); } }; let props = { onZooInst: { bar() { return " [bar on overridden instance] " + this.bar(); } } }; let z = new Zoo(); let reflectBar = Reflect.apply(props.onZooInst.bar, z.__proto__, []); let callBar = props.onZooInst.bar.call(z.__proto__); console.log(reflectBar); console.log(callBar); 
0
source

Source: https://habr.com/ru/post/1271951/


All Articles