Class Constructor Restrictions

91 views
Skip to first unread message

Si Robertson

unread,
Mar 12, 2015, 6:47:33 PM3/12/15
to streng...@googlegroups.com
From the proposal...

  It is a (early) ReferenceError to use ‘this’ in a class’s constructor, other than as the
  object targeted in a property assignment. All such assignments have to be expression
  statements directly in the statement list of the constructor body.

That sounds far too restrictive to me, some classes may need to initialize themselves by doing more than simply assigning values to properties. For example...

  class MyAwesomeEventSynchronizer {
    constructor(target) {
      // property assignment goes here

      target.addEventListener("mousedown", this.handleMouseDown); // error?
      target.addEventListener("mouseup", this.handleMouseUp); // error?

      window.requestAnimationFrame(this.flushEvents); // error?
    }

    // functions go here
  }

How is this kind of situation going to be dealt with in strong mode?

Andreas Rossberg

unread,
Mar 13, 2015, 6:52:59 AM3/13/15
to Si Robertson, streng...@googlegroups.com
On 12 March 2015 at 23:47, Si Robertson <retrom...@gmail.com> wrote:
From the proposal...

  It is a (early) ReferenceError to use ‘this’ in a class’s constructor, other than as the
  object targeted in a property assignment. All such assignments have to be expression
  statements directly in the statement list of the constructor body.

That sounds far too restrictive to me, some classes may need to initialize themselves by doing more than simply assigning values to properties.

Yes, this definitely is a serious pain point. We are fully aware that this is overly restrictive, and would like to relax it. But so far we don't have a good solution that wouldn't allow half-constructed objects to escape from constructors arbitrarily, and thereby break most invariants we'd like to establish. So to get moving, we erred on a conservative semantics for the time being. We certainly intend to revisit this point in the future (see also the short discussion in the strawman).

/Andreas

Si Robertson

unread,
Mar 13, 2015, 11:01:26 AM3/13/15
to streng...@googlegroups.com, retrom...@gmail.com
... so far we don't have a good solution that wouldn't allow half-constructed objects to escape from constructors arbitrarily ...

Would passing a method out from a class constructor after the class properties have been dealt with pose a half-constructed-object risk? I can see a potential problem occurring if a class method is invoked prior to all class properties being assigned values but once the latter has been done the class/object would be stable enough, wouldn't it?

Dmitry Lomov

unread,
Mar 13, 2015, 11:07:00 AM3/13/15
to Si Robertson, streng...@googlegroups.com
On Fri, Mar 13, 2015 at 4:01 PM, Si Robertson <retrom...@gmail.com> wrote:
... so far we don't have a good solution that wouldn't allow half-constructed objects to escape from constructors arbitrarily ...

Would passing a method out from a class constructor after the class properties have been dealt with pose a half-constructed-object risk? I can see a potential problem occurring if a class method is invoked prior to all class properties being assigned values but once the latter has been done the class/object would be stable enough, wouldn't it?

We have to deal with subclassing as well:

```js
class Base {
    constructor() {
        this.x = 42;
        process();
    }
    process() { ... }
}

class Derived extends Base {
   constructor() {
       super();
       this.y = 83;
   }
   process() { ... use this.y in some way ... }
}
```
 


On Friday, 13 March 2015 10:52:59 UTC, Andreas Rossberg wrote:
On 12 March 2015 at 23:47, Si Robertson <retrom...@gmail.com> wrote:
From the proposal...

  It is a (early) ReferenceError to use ‘this’ in a class’s constructor, other than as the
  object targeted in a property assignment. All such assignments have to be expression
  statements directly in the statement list of the constructor body.

That sounds far too restrictive to me, some classes may need to initialize themselves by doing more than simply assigning values to properties.

Yes, this definitely is a serious pain point. We are fully aware that this is overly restrictive, and would like to relax it. But so far we don't have a good solution that wouldn't allow half-constructed objects to escape from constructors arbitrarily, and thereby break most invariants we'd like to establish. So to get moving, we erred on a conservative semantics for the time being. We certainly intend to revisit this point in the future (see also the short discussion in the strawman).

/Andreas

--
You received this message because you are subscribed to the Google Groups "Strengthen JS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to strengthen-j...@googlegroups.com.
To post to this group, send email to streng...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/strengthen-js/5b2f775d-7277-4b42-8ea7-cc76c7ee7e1f%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Andreas Rossberg

unread,
Mar 13, 2015, 11:07:44 AM3/13/15
to Si Robertson, streng...@googlegroups.com
On 13 March 2015 at 16:01, Si Robertson <retrom...@gmail.com> wrote:
... so far we don't have a good solution that wouldn't allow half-constructed objects to escape from constructors arbitrarily ...

Would passing a method out from a class constructor after the class properties have been dealt with pose a half-constructed-object risk? I can see a potential problem occurring if a class method is invoked prior to all class properties being assigned values but once the latter has been done the class/object would be stable enough, wouldn't it?

The problem is inheritance. Even at the end of a constructor you cannot generally assume that the object is complete, since a subclass may add further properties. And through overriding, its methods could then see uninitialised instances. E.g.:

  class C {
    constructor() { this.a = 0; this.m() }
    m() { print(this.a) }
  }
  class D extends C {
    constructor() { super(); this.b = 1 }
    m() { print(this.b) }  // oops
  }

Worse, you could even have an evil subclass:

  class E extends C {
    constructor() {}
    m() { this.evil = 666 }  // sneak in my own property before the instance is sealed, huah!
  }

/Andreas

Si Robertson

unread,
Mar 13, 2015, 11:31:55 AM3/13/15
to streng...@googlegroups.com, retrom...@gmail.com
Understood, thanks guys. This is evidently a much bigger problem than it initially appeared to be. It's a shame ES6 (or whatever it's called now) didn't move property declarations outside of the constructor, but I guess that would be broken backward compatibility.

Si Robertson

unread,
May 25, 2015, 9:01:12 AM5/25/15
to streng...@googlegroups.com
I'm still not keen on this restriction.

Over the last couple of months I have been keeping tabs on the number of times I use this within a class constructor, beyond property assignment, and it's quite a lot. The use of Object.freeze(), Object.seal(), and Object.defineProperty() within a class constructor appears to the be main reason for me passing this around. There are also a few libraries of mine that keep hold of object references until those objects are revoked, and that also involves passing this outside of the class constructor.

How many other OOP based programming languages enforce a similar restriction?

Andreas Rossberg

unread,
Jun 1, 2015, 8:38:02 AM6/1/15
to Si Robertson, streng...@googlegroups.com
On 25 May 2015 at 15:01, Si Robertson <retrom...@gmail.com> wrote:
I'm still not keen on this restriction.

Neither are we. :)
 

Over the last couple of months I have been keeping tabs on the number of times I use this within a class constructor, beyond property assignment, and it's quite a lot. The use of Object.freeze(), Object.seal(), and Object.defineProperty() within a class constructor appears to the be main reason for me passing this around. There are also a few libraries of mine that keep hold of object references until those objects are revoked, and that also involves passing this outside of the class constructor.

These are all good use cases (although Object.seal should be unnecessary in strong mode, since it already is implied).

To be honest, we didn't have time to think about this problem much lately, so I cannot come up with any news yet. Clearly, the real solution would be actual property declarations, and there is some chance that we might get them in ES relatively soon. But until then, we need to find some stop-gap solution.

/Andreas


How many other OOP based programming languages enforce a similar restriction?


On Thursday, 12 March 2015 22:47:33 UTC, Si Robertson wrote:
From the proposal...

  It is a (early) ReferenceError to use ‘this’ in a class’s constructor, other than as the
  object targeted in a property assignment. All such assignments have to be expression
  statements directly in the statement list of the constructor body.

That sounds far too restrictive to me, some classes may need to initialize themselves by doing more than simply assigning values to properties. For example...

  class MyAwesomeEventSynchronizer {
    constructor(target) {
      // property assignment goes here

      target.addEventListener("mousedown", this.handleMouseDown); // error?
      target.addEventListener("mouseup", this.handleMouseUp); // error?

      window.requestAnimationFrame(this.flushEvents); // error?
    }

    // functions go here
  }

How is this kind of situation going to be dealt with in strong mode?

--
You received this message because you are subscribed to the Google Groups "Strengthen JS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to strengthen-j...@googlegroups.com.
To post to this group, send email to streng...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages