> On Feb 6, 2015, at 10:35 AM, Claude Pache <[email protected]> wrote:
> 
>> 
>> Le 6 févr. 2015 à 18:04, Ben Newman <[email protected] 
>> <mailto:[email protected]>> a écrit :
>> 
>> The specific line in rev32 of the spec that prevents [[Call]]ing 
>> "classConstructor" functions is 9.2.2.2 
>> <https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist>:
>> 
>> 2. If F’s [[FunctionKind]] internal slot 
>> <https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object-internal-methods-and-internal-slots>
>>  is "classConstructor", throw a TypeError exception.
>> 
>> From my reading of the spec, I think the idiomatic Foo.call(this) pattern 
>> that Luke Scott described would work if we simply changed that line to 
>> something slightly weaker:
>> 
>> 2. If F’s [[FunctionKind]] internal slot 
>> <https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object-internal-methods-and-internal-slots>
>>  is "classConstructor" and InstanceofOperator 
>> <https://people.mozilla.org/~jorendorff/es6-draft.html#sec-instanceofoperator>(thisArgument,
>>  F) is false, throw a TypeError exception.
>> 
>> This mirrors an assertion discipline that has saved me from many bugs due to 
>> forgetting the new operator:
>> 
>> function Base() {
>>   assert.ok(this instanceof Base);
>>   ...
>> }
>> 
>> function Derived() {
>>   assert.ok(this instanceof Derived);
>>   Base.call(this);
>>   ...
>> }
>> 
>> Derived.prototype = Object.create(Base.prototype, {
>>   constructor: { value: Derived, ... }
>> });
>> 
>> Is the addition of the instanceof check naive? Would it invalidate any of 
>> the assumptions involved in the invocation of F?
>> 
>> I'm happy to file a bug if this change merits further consideration.
>> 
>> It may be worth noting that only constructors created by class syntax will 
>> have their [[FunctionKind]] internal slot set to "classConstructor", so 
>> (even with the current spec) you can still invoke ordinary constructor 
>> functions using [[Call]]. However, it seems regrettable that you have to 
>> know whether a constructor was created by class syntax in order to know 
>> whether the Foo.call(this) pattern is safe.
> 
> The issue is deeper. In short, you cannot feed an ES6-class constructor with 
> an already allocated object, whatever that object is.
> 
> With a user-defined good ol' pre-ES6 constructor:
> 
>     foo = new Foo // allocate a Foo object and initialise it
>     
> is usually equivalent to:
> 
>     foo = Object.create(Foo.prototype) // allocate a Foo object ...
>     Foo.call(foo) // ... and initilise it
> 
> With the new ES6-class semantics, for the sake of subclassability of 
> builtins, it is, on purpose, not possible to separate allocation from 
> initialisation that way. Even before ES6, builtin classes, did not support 
> such a pattern, e.g.,
> 
>     arr = new Array(2, 3) // allocate a new array and initialise it
> 
> is in no way equivalent to:
> 
>     arr = Object.create(Array.prototype) // allocate an new object, but it 
> won't be an array...
>     Array.call(arr, 2, 3) // ... and don't initialise it, but create 
> uselessly a new Array
>     
> And it appeared that introducing the possibility for builtins to have 
> separate allocation and initialisation phases was problematic.
> 
> —Claude

The following should be sufficient with the current ES6, spec, right? It 
currently works in 6to5.

function mixin(classObject, …traits) {
    var newClassObject = class extends classObject{}
    // … fill in prototype here
}

Below is a possible workaround for pre-ES6 code (without being transpired), 
provided that an Object.isClass is added (checks for internal classConstructor 
flag), which would always return false in pollyfills.

function mixin(classObject, traits) {
    var newClassObject;
    if (Object.isClass(classObject)) {
        newClassObject = eval("class extends classObject{}”);
    } else {
        newClassObject = function() {classObject.apply(this, arguments);};
        newClassObject.prototype = Object.create(classObject.prototype);
        newClassObject.prototype.constructor = newClassObject;
    }
    // … fill in prototype here
}

It’s an ugly hack. Unfortunately the eval is necessary because Chrome throws 
“Unexpected reserved word” for the class keyword.

--
Luke

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to