archives

« Bugzilla Issues Index

#3136 — 19.1.1.1 Object: Change ObjectCreate to OrdinaryCreateFromConstructor?


19.1.1.1 Object ( [ value ] ):

Step 1 currently calls `ObjectCreate(%ObjectPrototype%)`, maybe it should be changed to `OrdinaryCreateFromConstructor(F, %ObjectPrototype%)` where F is the currently active function.


Also see 19.2.1.1 step 14b, 22.1.1.1 step 5b, 19.5.1.1 step 3a etc. where OrdinaryCreateFromConstructor or similar operations are already used.


I don't think so. This is a specific definition for Object, called as a function, and isn't intended to be subclassed. Parameterizing it with via F.prototype may also have legacy compatibility issues.


I don't understand why 19.2.1.1 Function or 19.5.1.1 Error use OrdinaryCreateFromConstructor (resp. GetPrototypeFromConstructor in 19.2.1.1), instead of simply using the intrinsic prototype object. And why 19.1.1.1 Object is allowed to use the intrinsic prototype object.

When we look at 19.5.1.1 Error, step 3a calls OrdinaryCreateFromConstructor(func, %ErrorPrototype%, ([[ErrorData]])) where `func` is the currently active function, so `func` is either the intrinsic Error function or a cloned Error function. No other case is possible here. In the case that `func` is the intrinsic Error function, OrdinaryCreateFromConstructor will always use the intrinsic Error prototype object since the "prototype" property on Error is non-writable + non-configurable. If `func` is a cloned Error function, OrdinaryCreateFromConstructor needs to retrieve the value of "prototype" dynamically at runtime.

So the only benefit (?) of using OrdinaryCreateFromConstructor in 19.5.1.1 Error (instead of using ObjectCreate) is that cloned Error functions can set a different prototype object.

Changing 19.1.1.1 to use OrdinaryCreateFromConstructor instead of ObjectCreate only has an effect for cloned Object functions.


(In reply to André Bargull from comment #2)
> I don't understand why 19.2.1.1 Function or 19.5.1.1 Error use
> OrdinaryCreateFromConstructor (resp. GetPrototypeFromConstructor in
> 19.2.1.1), instead of simply using the intrinsic prototype object. And why
> 19.1.1.1 Object is allowed to use the intrinsic prototype object.
>
> When we look at 19.5.1.1 Error, step 3a calls
> OrdinaryCreateFromConstructor(func, %ErrorPrototype%, ([[ErrorData]])) where
> `func` is the currently active function, so `func` is either the intrinsic
> Error function or a cloned Error function. No other case is possible here.
> In the case that `func` is the intrinsic Error function,
> OrdinaryCreateFromConstructor will always use the intrinsic Error prototype
> object since the "prototype" property on Error is non-writable +
> non-configurable. If `func` is a cloned Error function,
> OrdinaryCreateFromConstructor needs to retrieve the value of "prototype"
> dynamically at runtime.
>
> So the only benefit (?) of using OrdinaryCreateFromConstructor in 19.5.1.1
> Error (instead of using ObjectCreate) is that cloned Error functions can set
> a different prototype object.

The concern is about subclassing more than cloning, although there are similarities between those two scenarios. There is also a need to balance legacy compatibility against ES6 sublcassing capabilities.

In the case, of Error, it is reasonable to assume that programmer will define subclasses of Error, via
class MyError extends Error {};

Such a subclass uses the Error constructor, but needs to set the new instance prototype to MyError.prototype. So we use OrdinaryCreateFromConstructor. There is a small chance of an incompatibility for legacy code, but the risk seems low compared to the importance of the new functionality.

>
> Changing 19.1.1.1 to use OrdinaryCreateFromConstructor instead of
> ObjectCreate only has an effect for cloned Object functions.

Object, on the other hand, is not intended to be explicitly subclassed. You normally say
class MyClass {};
instead of
class MyClass extends Object {};

There is special case semantics for class definitions that makes sure that the Object constructor is not called for Subclasses like MyClass. So, we don't have to worry (too much) about 19.1.1.1 being super called in a subclass constructor.

On the other hand, 19.1.1.1 was specified prior to ES6 to creates objects with their [[Prototype]] set to the intrinsic Object.prototype. My guess is that there is a much higher chance that legacy code exists that depends upon that specific behavior.

So, 19.1.1.1 preserves legacy behavior, while 19.2.1.1 and 19.5.1.1 (and really all legacy constructors except for Object) is taking a risk at relaxing legacy behavior in order to facilitate subclassing.


(In reply to Allen Wirfs-Brock from comment #3)
> The concern is about subclassing more than cloning, although there are
> similarities between those two scenarios. There is also a need to balance
> legacy compatibility against ES6 sublcassing capabilities.
>

I still don't understand how subclassing comes into play here. :-(

If step 1 of 19.1.1.1 is changed to:
> 1. If value is null, undefined or not supplied, then
> a. Let func be the active function object.
> b. Return OrdinaryCreateFromConstructor(func, %ObjectPrototype%).

there will be no difference for the intrinsic Object constructor function when compared to the current step 1 of 19.1.1.1.


I do _not_ suggest to change 19.1.1.1 to:
> 1. If value is null, undefined or not supplied, then
> a. Let func be the this value.
> b. Return OrdinaryCreateFromConstructor(func, %ObjectPrototype%).

Such a change will have effects on subclassing and legacy behaviour and therefore should not be applied.


fixed in rev31 editor's draft


In Rev31