Allen Wirfs-Brock wrote:
I think the real issue is whether we want to encourage the style of
constructor where "called as a constructor" and "called as a function"
do completely different things. I'm leaning towards the conclusions that
we shouldn't. One reason is that the language is fighting so hard
against that pattern. Anything that requires multiple low level hacks to
the language semantics (and possibly new syntax) to make it "just work"
probably is something that shouldn't be done.
Well, I would disagree strongly.
The "problem" here stems from tightky coupling the constructor function
to the "class". Those two things can be very cleanly and minimally
sepaerated.
Read my "Good bye constructor functions?" post from Dec 30. This pattern
(class = constructor) is very deeply grown into thinking of JS
developer, but it is not the law. It is very easy to get rid of it.
Then, nothing from below is ever needed. It all follows naturally. Anf
without hacks.
For the non-spec readers out there, here is the jury rigging pattern.
Every "class" in the spec. that has different constructor/function call
behavior has (will have) a @@create method that allocates its instances
and in the process tags the instance with a brand and perhaps a not yet
initialized flag. In a new expression, the @@create call happens before
the call to the actual constructor function and the result of the
@@create call is what is passed as the this value to the constructor.
The constructor function, itself, is defined to determine its behavior
based upon the this value that is passed to it. It the this value is
branded with the instance brand, the constructor initializes and returns
the instance according to its normal definition. Otherwise, if the this
value is not an object or an object without the instance brand then the
constructor performs the "call" functionality.
For the existing built-in this jury rig appears to be adequate for
ensuring compatibility with all existing code. Consider all the ways
that, for example, Number can currently be "called as a function":
let n;
n = Number("1234"); //this is undefined so jury rig takes ToNumber path
within constructor.
n = Number.call(null, "1") //this is null so jury rig takes ToNumber
path within constructor.
n = Number.call(1, "1") //this is not an object so jury rig takes
ToNumber path within constructor.
n = Number.call(new Object, "1"); //this is an object without the number
brand so jury rig takes ToNumber path within constructor.
n = {foo: Number}.foo("1"); //this is an object without the number brand
so jury rig takes ToNumber path within constructor.
n = Number.call(new Number("2"), "1"); //this is an object with the
number brand but it is already initialized so (for backwards compat)
jury rig takes ToNumber path within constructor.
Number.prototype.bar = Number;
n=new Number(2).bar("1"; //this is an object with the number brand but
it is already initialized so jury rig takes ToNumber path within
constructor.
But in ES6 (now backwards compat issues here):
class MyNumber extends Number {
additionalNumberMethod () {}
}
n = new MyNumber("1"); //default constructor does super.constructor call
with new instance created by Number.@@create, so initialization path
taken with Number.
n = MyNumber("1") //default constructor does super.constructor call with
undefined as this value, so ToNumber path taken and Number(1) is returned.
But if MyNumber wants to also expose its own explicit conversions
behavior it will need to provide its own constructor that decides when
to take that path
Here is a ES level example, of defining a class that uses the "called
same as new" pattern:
const $myBrand = new Symbol(true);
import $create ...;
class MyObj {
constructor () {
if (typeof this !== 'object' || this == null || !Reflect.has($myBrand))
return new MyObj();
/* could check here for this already initialized */
/* do any initialization */
this[$myBrand] = true; //tag as initialized
}
}
Object.mixin(MyObj, {
[$create] () {
let obj = super(); //call default @@create
Object.defineOwnProperty(obj,$myBrand,{value: undefined, writable: true});
return obj;
}
});
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss