Instance constructors

# David Bruant (14 years ago)

Currently, WebIDL states (4.5.3): "There MUST exist an interface prototype object www.w3.org/TR/2011/WD-WebIDL-20110712/#dfn-interface-prototype-object

for every interface www.w3.org/TR/2011/WD-WebIDL-20110712/#dfn-interface defined (...)www.w3.org/TR/2011/WD-WebIDL-20110712/#dfn-extended-attribute.

The interface prototype object for a particular interface has properties that correspond to the attributes www.w3.org/TR/2011/WD-WebIDL-20110712/#dfn-attribute and

operations www.w3.org/TR/2011/WD-WebIDL-20110712/#dfn-operation

defined on that interface."

This is likely to be motivated by the need to enforce that all "instances" inherit all constants, attributes and operation from the interface. This is fine for operations and constants but forces something i consider hacky for attributes which is imposing them as getter/setters on the interface and use the |this| to emulate a value-per-instance behavior.

One problem this hack is a workaround of is that JavaScript lacks of a mechanism to enforce that things inheriting from the same prototype object have some common properties. When running "Object.create(someObject);", someObject has no way to initialize the object being created while it may be relevant for it to add properties.

I'd like to propose the addition of a new internal property of object which is [[Initializer]] which is a function that is called when an object is created. Example:

// assuming o is an object with o [[Initializer]] = function(){this.a = 1} // Maybe it's a regular object, maybe it's an host. Whatever. var oo = Object.create(o); // calling o.[[Initializer]] with the object being created as |this|

Object.getOwnPropertyDescriptor(o, 'a'); // {value: 1} (+true, true, true)

Actually the idea of an internal [[Initializer]] is not very far from the "constructor" in the classes proposal [1]. I would expect "super" to work the same which is calling [[Prototype]].[[Initializer]]. I think that [[Initializer]] should be assigned at initialization time and not be settable afterward, like for [[Prototype]]. Maybe could it be assigned with an additional Object.create argument/obj init syntax special field?

Open issue:

  • How to pass arguments to [[Initializer]]? (is it absolutely necessary?)

Back to WebIDL, it could define custom [[Initializer]] for each interface in order to create the relavant properties at the own layer as data properties rather than the craziness of getter/setter at the interface level.

David

[1] harmony:classes

# Allen Wirfs-Brock (14 years ago)

let instance = Object.create(o); instance.constructor(/* constructor args */)

This assumes that the new instance will inherit "constructor" from its prototype chain (possibly all the way to Object.prototype.constructor (.i.e. Object). If you are careful to always code your constructors to return this, you can shorten it to:

let instance = Object.create(o).constructor(/* constructor args */);

As I think I have talked about in the past. For ES.next I think we should extend the semantics of new to be approximately:

function ESNewOperator( rhs, args) { if (rhs has [[Constructor]] ) return rhs.[[Constructor]].apply(undefined,args); let instance = Object.create(rhs); instance.constructor(...args); return instance; } ( a few additional error conditions need to be added)

This allows the new operator to make sense (and work in an internally interoperable manner) for both prototypical and classical inheritance

For example:

let proto = { depth: 0; constructor: function() {this.depth += 1}; /* get parent's depth, increment it, and store as own property of instance */ }

let lev1 = new proto; let lev2 = new lev1; let lev3 = new lev2;

I'm being terse in my explanation, but this would essentially giving ES the ability to really do self-style prototypical instantiation without getting in the way to existing constructor-based instantiation. Newing an arbitrary object creates a new instance whose [[Prototype]] is the newed object and then calls the inherited constructor. The constructor method servers as the initialize method. Such a constructor can be called either explicitly as has traditionally been done in JS or it call be called implicitly by newing an that inherits its as its constructor property.

Regarding, WebIDL. It seems to me, that it just needs an extended attribute to specifies which attributes are instance attributes rather than prototype attributes.

# David Bruant (14 years ago)

Le 24/09/2011 00:35, Allen Wirfs-Brock a écrit :

let instance = Object.create(o); instance.constructor(/* constructor args */)

This assumes that the new instance will inherit "constructor" from its prototype chain (possibly all the way to Object.prototype.constructor (.i.e. Object). If you are careful to always code your constructors to return this, you can shorten it to:

let instance = Object.create(o).constructor(/* constructor args */);

As I think I have talked about in the past. For ES.next I think we should extend the semantics of new to be approximately:

function ESNewOperator( rhs, args) { if (rhs has [[Constructor]] ) return rhs.[[Constructor]].apply(undefined,args); let instance = Object.create(rhs); instance.constructor(...args); return instance; } ( a few additional error conditions need to be added)

This allows the new operator to make sense (and work in an internally interoperable manner) for both prototypical and classical inheritance

For example:

let proto = { depth: 0; constructor: function() {this.depth += 1}; /* get parent's depth, increment it, and store as own property of instance */ }

let lev1 = new proto; let lev2 = new lev1; let lev3 = new lev2;

I love this!

# Cameron McCormack (14 years ago)

On 24/09/11 8:35 AM, Allen Wirfs-Brock wrote:

Regarding, WebIDL. It seems to me, that it just needs an extended attribute to specifies which attributes are instance attributes rather than prototype attributes.

I think it only makes sense to have IDL attributes correspond to properties on the instance itself if the property can be a data property, and that if it can be modelled with a data property then it needs to have no special behaviour when being assigned to (no type coercions due to IDL, no other behaviour), unless we want to require JS implementations to use proxies for these objects.

I don't think many attributes in Web APIs fall into this category.

# David Bruant (14 years ago)

Le 11/10/2011 05:48, Cameron McCormack a écrit :

On 24/09/11 8:35 AM, Allen Wirfs-Brock wrote:

Regarding, WebIDL. It seems to me, that it just needs an extended attribute to specifies which attributes are instance attributes rather than prototype attributes.

I think it only makes sense to have IDL attributes correspond to properties on the instance itself if the property can be a data property, and that if it can be modelled with a data property then it needs to have no special behaviour when being assigned to (no type coercions due to IDL, no other behaviour)

Oh true! Current WebIDL 4.5.5 Attribute, for the setter, step 6 "Let idlValue be the result of converting V to an IDL value.". So, DOM attributes are typed and the typing is enforced by their ECMAScript representation.

unless we want to require JS implementations to use proxies for these objects.

Indeed. I hadn't noticed the type enforcement. I take back all what I said about DOM attributes being data property. A setter with type enforcement sounds like a better idea than proxies returning data descriptor.

It gives me an idea for a proxy library which would be to pass a custom field in a data descriptor indicating the type. The proxy would enforce the type under the hood and either coerce silently or throw if the type is incorrect.

# Mark S. Miller (14 years ago)

On Tue, Oct 11, 2011 at 11:43 AM, David Bruant <david.bruant at labri.fr>wrote:

It gives me an idea for a proxy library which would be to pass a custom field in a data descriptor indicating the type. The proxy would enforce the type under the hood and either coerce silently or throw if the type is incorrect.

That's a cool idea, and a good way to experiment with possible future (post ES6) direct representation of type-constrained (or guard constrained) properties.

# Tom Van Cutsem (14 years ago)

2011/10/11 Mark S. Miller <erights at google.com>

On Tue, Oct 11, 2011 at 11:43 AM, David Bruant <david.bruant at labri.fr>wrote:

It gives me an idea for a proxy library which would be to pass a custom field in a data descriptor indicating the type. The proxy would enforce the type under the hood and either coerce silently or throw if the type is incorrect.

That's a cool idea, and a good way to experiment with possible future (post ES6) direct representation of type-constrained (or guard constrained) properties.

Indeed, and another argument in favor of copying over non-standard property attributes to normalized property descriptors crossing the defineProperty / getOwnPropertyDescriptor trap boundaries.

# Brendan Eich (14 years ago)

On Oct 11, 2011, at 5:43 AM, David Bruant wrote:

It gives me an idea for a proxy library which would be to pass a custom field in a data descriptor indicating the type. The proxy would enforce the type under the hood and either coerce silently or throw if the type is incorrect.

The original DOM in Netscape 2 did this -- native data structures had proxy-like wrappers with get and set traps, to use Proxy terminology. The Netscape layout engine built these structures quickly and during progressive rendering (remember dial-up?). I did not want to reify JS objects for each eagerly, so used proxy-like wrappers created on demand. The source of truth was in the C (no portable C++ back then) data structures, so I needed get and set traps. This was before accessor properties, so the resulting "DOM attributes" looked like data properties, but did not behave as if they were data properties.