Issue sett

30 views
Skip to first unread message

Trevor Norris

unread,
Apr 23, 2015, 4:50:16 PM4/23/15
to streng...@googlegroups.com
With only ES6 it's impossible to set non-method class properties in the class declaration. Which is an issue on it's own, but we'll ignore it for now and I'll use some pseudo-ES7 syntax to pretend it works.

Take the following constructor:

class Readable {
  constructor() {
    this._onreadable = null;
    // Acquire the default onreadable callback if it's been set.
    if (typeof this.constructor._onreadable === 'function')
      this._onreadable = this.constructor._onreadable;
  }
  static onreadable(cb) {
    if (typeof cb !== 'function')
      throw new TypeError('callback must be a function');
    this._onreadable = cb;
  }
  // As if I had done Readable._onreadable = ... afterwards.
  static _onreadable = undefined;
}

Now say I want to extend this:

class MyReadable extends Readable {
  constructor() {
    super();
  }
}

Then set the static value afterwards:

MyReadable.onreadable(function foo() { });

The issue here is that _onreadable is defined in the prototype chain, which means the class constructor will need to be created like so:

class MyReadable extends Readable {
  constructor() {
    super();
  }
  // Make sure this is set.
  static _onreadable = undefined;
}

This is a usability issue. It requires that every inherited instance of my class know about and set the static properties. I'm just curious if this is just an accepted issue, or if something will be done about it?

Trevor Norris

unread,
Apr 23, 2015, 4:51:18 PM4/23/15
to streng...@googlegroups.com
FYI: Sorry. Didn't notice I hadn't finished writing the title of the post but also figure it would be stupid to delete and re-post.

Sébastien Doeraene

unread,
Apr 23, 2015, 5:19:44 PM4/23/15
to Trevor Norris, streng...@googlegroups.com
Hi,

First, note that you can't read `this.constructor` in your constructor() in the first place, since that's not a valid use of `this` in a constructor(), AFAICT. Maybe you could use `new.target` for that purpose, which does not seem to be forbidden in strong mode.

Otherwise, it seems to me that this restriction is similar to other restrictions imposed by strong mode. I don't see why a class value should be treated differently from any other strong object.

Cheers,
Sébastien

--
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/389ef01d-8815-4894-a27c-7a4569f1db19%40googlegroups.com.

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

Trevor Norris

unread,
Apr 23, 2015, 5:39:27 PM4/23/15
to Sébastien Doeraene, streng...@googlegroups.com
"Can't read" as in shouldn't, or strong mode won't allow it? Because I've been using that code all day in latest d8 v4.4. So it does work, as in, it will execute.

And I'm aware of the restriction. The point is that the restriction is a usability issue. Requiring that every inheriting class duplicate all my internal properties, and any other internal properties in the chain, is a usage fail for anyone that wants to implement my class.

Sébastien Doeraene

unread,
Apr 23, 2015, 5:46:52 PM4/23/15
to Trevor Norris, streng...@googlegroups.com
Can't read as it won't allow it. It's an early error. From the strawman:

It is a (early) ReferenceError to use ‘this’ in a class’s constructor, other than as the object targeted in a property assignment.

Keep in mind that strong mode is only very partially implemented in V8. So "it works now" is not a very strong argument to say that something is valid in strong mode. Only the strawman can be trusted, so far.

Cheers,
Sébastien

Trevor Norris

unread,
Apr 23, 2015, 5:56:54 PM4/23/15
to Sébastien Doeraene, streng...@googlegroups.com
On Thu, Apr 23, 2015 at 3:46 PM, Sébastien Doeraene <sjrdo...@gmail.com> wrote:
Can't read as it won't allow it. It's an early error. From the strawman:

It is a (early) ReferenceError to use ‘this’ in a class’s constructor, other than as the object targeted in a property assignment.

Keep in mind that strong mode is only very partially implemented in V8. So "it works now" is not a very strong argument to say that something is valid in strong mode. Only the strawman can be trusted, so far.

The point of my question was to verify how V8 operated vs the strawman. Not to say the proposal should operate like d8 does today.

The reason for this post originally was to point out that there is a usability issue with the strawman and to see if it's just accepted that all inheriting classes will need to get used to duplicating all those static properties, or if there was something else that could be done to mitigate the usability issue. And if they need to duplicate all those static properties if there will be a standard way of doing that, or if the community will need to come up with their own way of doing it.

Sébastien Doeraene

unread,
Apr 24, 2015, 8:24:15 AM4/24/15
to Trevor Norris, streng...@googlegroups.com
Hi again,

I thought a bit more about your issue. IIUC, you can solve your usability issue with a WeakMap mapping Classes (constructor's) to their _onreadable field:

const _onreadableMap = new WeakMap();
class Readable {
  constructor() {
    const clzOnreadable = _onreadableMap.get(new.target);
    this._onreadable = typeof clzOnReadable === 'function' ? clzOnreadable : null;
  }
  static onreadable(cb) {
    if (typeof cb !== 'function')
      throw new TypeError('callback must be a function');
    _onreadableMap.set(this, cb);
  }
}

Would that help?

Cheers,
Sébastien

Andreas Rossberg

unread,
Apr 24, 2015, 8:55:49 AM4/24/15
to Trevor Norris, streng...@googlegroups.com
Let me try to understand your question a little bit better. It seems you are making a couple of assumptions that I don't quite understand.

First of all, ES6 has no static fields, other than methods and getters/setters. You assume the addition of one, plus some specific semantics that I don't quite follow. In particular, why do you think that the static initialisation of the class would not have been executed when you call the constructor? I think the only way for this to happen would be if the static code of the class (assuming ES6 even allowed such code) would itself call the constructor.

Second, I think _onreadable is really meant to be private, right? We are currently discussing the addition of private members to ES7, but that's further out. In the mean time, _static_ privacy can be easily achieved through modules -- being private anyway there is no reason that _onreadable has to be a property at all. For example:

------------
let onreadable;  // not exported!

export class Readable {
  constructor() {
    this._onreadable = null;
    // Acquire the default onreadable callback if it's been set.
    if (typeof onreadable === 'function')
      this._onreadable = onreadable;
  }
  static onreadable(cb) {
    if (typeof cb !== 'function')
      throw new TypeError('callback must be a function');
    onreadable = cb;
  }
}
------------

Would that work? Note that it actually properly hides the static onreadable state, so arguably is an improvement.

/Andreas

Andreas Rossberg

unread,
Apr 24, 2015, 8:59:45 AM4/24/15
to Trevor Norris, streng...@googlegroups.com
Or indeed use Sebastian's solution if the intention was to have a separate mapping for each subclass. Or do you need this to be inherited as well?

In any case, I think this is more an issue with ES6 classes than with strong mode in particular.

/Andreas

Bradley Meck

unread,
Apr 24, 2015, 10:43:16 AM4/24/15
to streng...@googlegroups.com, trev....@gmail.com
I believe each class should have the inherited value unless overwritten.


In any case, I think this is more an issue with ES6 classes than with strong mode in particular.


ES6 supports this format using `this.constructor[blah]` right now, the question is how to do this in a manner that strong mode supports.

Erik Arvidsson

unread,
Apr 24, 2015, 11:03:14 AM4/24/15
to Bradley Meck, streng...@googlegroups.com, trev....@gmail.com
Strong mode does not disallow that (unless you are in the constructor
and in that case you can use new.target).

--
erik

Trevor Norris

unread,
Apr 24, 2015, 12:39:10 PM4/24/15
to streng...@googlegroups.com, trev....@gmail.com, Andreas Rossberg
On Friday, April 24, 2015 at 6:55:49 AM UTC-6, Andreas Rossberg wrote:
Let me try to understand your question a little bit better. It seems you are making a couple of assumptions that I don't quite understand.

My fault. Allow me to explain my use case by using some code. Using the Readable class I had earlier, say I do this:

class MyReadable1 extends Readable {
  constructor() { }
}

class MyReadable2 extends Readable {
  constructor() { }
}

MyReadable1.onreadable(function foo() { });
MyReadable2.onreadable(function bar() { });

Using the private variable method you outlined I would get the following:

print(MyReadable1._onreadable);  // function bar() { }
print(MyReadable2._onreadable);  // function bar() { }

But the output I would like to see is:

print(MyReadable1._onreadable);  // function foo() { }
print(MyReadable2._onreadable);  // function bar() { }

The point of the example is to show that doing this is impossible in strong mode. I realize this isn't how normal OOP languages work, but it's a usability advantage for JS and libraries that want to allow setting default callbacks for newly constructed objects.

Erik Arvidsson

unread,
Apr 24, 2015, 1:14:19 PM4/24/15
to Trevor Norris, streng...@googlegroups.com, Andreas Rossberg
On Fri, Apr 24, 2015 at 12:39 PM, Trevor Norris <trev....@gmail.com> wrote:
> On Friday, April 24, 2015 at 6:55:49 AM UTC-6, Andreas Rossberg wrote:
>>
>> Let me try to understand your question a little bit better. It seems you
>> are making a couple of assumptions that I don't quite understand.
>
>
> My fault. Allow me to explain my use case by using some code. Using the
> Readable class I had earlier, say I do this:
>
> class MyReadable1 extends Readable {
> constructor() { }
> }
>
> class MyReadable2 extends Readable {
> constructor() { }
> }
>
> MyReadable1.onreadable(function foo() { });
> MyReadable2.onreadable(function bar() { });
>
> Using the private variable method you outlined I would get the following:
>
> print(MyReadable1._onreadable); // function bar() { }
> print(MyReadable2._onreadable); // function bar() { }
>
> But the output I would like to see is:
>
> print(MyReadable1._onreadable); // function foo() { }
> print(MyReadable2._onreadable); // function bar() { }

That is the output I see.

http://jsbin.com/modiduziyi/1/edit?html,console

>
> The point of the example is to show that doing this is impossible in strong
> mode. I realize this isn't how normal OOP languages work, but it's a
> usability advantage for JS and libraries that want to allow setting default
> callbacks for newly constructed objects.

I might be missing your point here. JS uses class side inheritance .

The problem with strong mode is that the function object created from
the class declaration is non extensible so adding the property will
not work without adding new syntax AND semantics.

>
> --
> 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/9b1fc7c1-40d3-4a10-9919-251a335d7e07%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.



--
erik

Trevor Norris

unread,
Apr 24, 2015, 1:49:45 PM4/24/15
to Erik Arvidsson, streng...@googlegroups.com, Andreas Rossberg
On Fri, Apr 24, 2015 at 11:13 AM, Erik Arvidsson <a...@google.com> wrote:
> That is the output I see.
>
> http://jsbin.com/modiduziyi/1/edit?html,console

It's the output you see because it's setting the _onreadable property
when onreadable() is called. Not when the class is created, and
changing a class after it's been created isn't allowed in strong mode
right? Using the private declaration you suggested would result in the
first printed output from my example.

> > The point of the example is to show that doing this is impossible in strong
> > mode. I realize this isn't how normal OOP languages work, but it's a
> > usability advantage for JS and libraries that want to allow setting default
> > callbacks for newly constructed objects.
>
> I might be missing your point here. JS uses class side inheritance .

I'm not sure what you mean by "class side inheritance".

> The problem with strong mode is that the function object created from
> the class declaration is non extensible so adding the property will
> not work without adding new syntax AND semantics.

Let me clarify, is it that the class can't be changed after it's been
declared or that the resulting object from the constructor can't be
mutated?

trev

Erik Arvidsson

unread,
Apr 24, 2015, 2:07:12 PM4/24/15
to Trevor Norris, streng...@googlegroups.com, Andreas Rossberg
On Fri, Apr 24, 2015 at 1:49 PM, Trevor Norris <trev....@gmail.com> wrote:
> On Fri, Apr 24, 2015 at 11:13 AM, Erik Arvidsson <a...@google.com> wrote:
>> That is the output I see.
>>
>> http://jsbin.com/modiduziyi/1/edit?html,console
>
> It's the output you see because it's setting the _onreadable property
> when onreadable() is called. Not when the class is created, and
> changing a class after it's been created isn't allowed in strong mode
> right? Using the private declaration you suggested would result in the
> first printed output from my example.
>
>> > The point of the example is to show that doing this is impossible in strong
>> > mode. I realize this isn't how normal OOP languages work, but it's a
>> > usability advantage for JS and libraries that want to allow setting default
>> > callbacks for newly constructed objects.
>>
>> I might be missing your point here. JS uses class side inheritance .
>
> I'm not sure what you mean by "class side inheritance".

class Base {}
class Derived extends Base {}
assert(Object.getPrototypeOf(Derived) === Base)

>> The problem with strong mode is that the function object created from
>> the class declaration is non extensible so adding the property will
>> not work without adding new syntax AND semantics.
>
> Let me clarify, is it that the class can't be changed after it's been
> declared

Yes

> or that the resulting object from the constructor can't be
> mutated?

The object is born non extensible so existing writable data properties
can be updated.

You cannot add a property to the function object represented by a
class in strong mode since it is born non extensible.

class C {
constructor() {
assert.throws(() => {
C.newProp = 1;
});
assert.throws(() => {
C.m = 2;
});
}
static m() {}
}
assert(!Object.isExtensible(C));
new C();

To make this work we need to add new syntax and semantics. We are
working on coming up with a workable design for class properties.
https://gist.github.com/jeffmo/054df782c05639da2adb

--
erik
Reply all
Reply to author
Forward
0 new messages