extends null
No, the problem is that
class C extends X {
when X turns out to be null must be an error along exactly these lines. Everywhere else in the language where X is accepted as an expression evaluated to a value, if X evaluates to null, this is the same thing as placing null in that position. Consider:
const NULL = null; // C nostalgia
class C extends NULL {
It would be awful for this to be different than
class C extends null {
If you really want to write a class whose prototype.__proto__
is null,
imperatively make it so after the class declaration. We don't need to
compromise the consistency of the language to make this rare case less ugly.
But it’s not an error! Either of the following two classes fail later, when you instantiate them, but not right away.
const X = null;
class C extends X {}
class D extends null {}
I’m arguing that both produce weird constructors. I’d much prefer an early error. Or dynamically switching from "derived" to "base" (but you seem to be saying that doing this dynamically is not a good idea).
On Sat, Feb 14, 2015 at 1:21 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
But it’s not an error! Either of the following two classes fail later, when you instantiate them, but not right away.
const X = null; class C extends X {} class D extends null {}
I didn't mean to imply an early error. The "extends X" can only produce a dynamic error, so I'm arguing that "extends null" should do the same.
I’m arguing that both produce weird constructors. I’d much prefer an early error.
You can't know early that X is null.
Or dynamically switching from "derived" to "base" (but you seem to be saying that doing this dynamically is not a good idea).
Dynamically switching from derived to base does not work, because our super semantics depends statically on the difference.
On 14 Feb 2015, at 22:26, Mark S. Miller <erights at google.com> wrote:
I didn't mean to imply an early error. The "extends X" can only produce a dynamic error, so I'm arguing that "extends null" should do the same.
When I say “early”, I don’t mean “static”, I mean: dynamically, when the class definition is evaluated, not later when the class is instantiated via new
.
I’m not seeing a dynamic error in the spec when the extends
clause is null (step 6e): people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-classdefinitionevaluation, people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-classdefinitionevaluation
Or dynamically switching from "derived" to "base" (but you seem to be saying that doing this dynamically is not a good idea).
Dynamically switching from derived to base does not work, because our super semantics depends statically on the difference.
Ah, checked statically, I wasn’t aware! Hadn’t found the check beforehand. Searched some more and it is indeed there, in 14.5.1.
On Sat, Feb 14, 2015 at 1:45 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
I’m not seeing a dynamic error in the spec when the
extends
clause is null (step 6e): people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-classdefinitionevaluation
That's a good suggestion. AFAICT, it would be an improvement. I don't see any downside.
Allen?
Making it a dynamic error at class definition time to extend null would
work but the motivation for not doing that was that someone might want to
create a class that has a {__proto__: null}
prototype. Personally, I would
be fine with saying that this case is so rare that it would be better to
have that dynamic error at class definition time.
Interesting. I have never seen this pattern and don’t see what it could be good for. Thus, a dynamic error at class definition time sounds good to me.
Interesting. I have never seen this pattern and don’t see what it could be good for. Thus, a dynamic error at class definition time sounds good to me.
The purpose would be defining a class whose instances don't have Object.prototype on their prototype chain. If "extends null" doesn't work, then I think you'd have to do something like this to achieve the same?
function NullBase() {}
NullBase.prototype = Object.create(null);
class C extends NullBase {}
That still wouldn't work at runtime because of the super semantics of C as a derived class. Instead
class C {....}
C.prototype.__proto__ = null;
Yes, it is ugly, but it is an odd case, so still obeys Kay's dictum:
"Simple things should be simple. Complex things should be possible" --Alan Kay
Rather than making "extends null" alone a runtime error at class evaluation time, is there a reason not to instead, make only a reference to "super" in the constructor of a class that extends null be a runtime error at class evaluation time?
Can't this be solved by returning a null object from the constructor?
class Null{
constructor() {
return Object.create(null);
}
}
class MyClass extends Null{
}
let foo = new MyClass();
foo.toString() //ReferenceError
No that would not work either. You want an object that has its [[Prototype]] set to MyClass.prototype.
Couldn't one of these be enough?
var Null = {prototype: {__proto__: null}};
var Null = {prototype: Object.create(null)};
class Foo extends Null {}
Is this the best way to use extends null
?
class C extends null {
constructor() {
let _this = Object.create(C.prototype);
return _this;
}
}
You can’t use this
, because it can’t be initialized via a super-constructor call: the super-constructor is Function.prototype
(which can’t be constructor-called).
Isn't super-constructor null in this case?
From step 4 in people.mozilla.org/~jorendorff/es6-draft.html#sec-getsuperconstructor, people.mozilla.org/~jorendorff/es6-draft.html#sec-getsuperconstructor
superConstructor is C.[GetPrototypeOf]
which should be null
after the class definition, if I'm not wrong. (But it finally throws due to type error, so technically speaking, you can't even reference the super constructor)
I can't think of a better way of extending null, but I wonder what's the use case? null usually represents lacking of a value, and extending the lacking of a value? My brain hurts.
Le 7 mai 2015 à 10:25, Axel Rauschmayer <axel at rauschma.de> a écrit :
Is this the best way to use
extends null
?
class C extends null { constructor() { let _this = Object.create(C.prototype); return _this; } }
No, you should say: Object.create(new.target.prototype)
Le 7 mai 2015 à 11:49, Glen Huang <curvedmark at gmail.com> a écrit :
Isn't super-constructor null in this case?
From step 4 in people.mozilla.org/~jorendorff/es6-draft.html#sec-getsuperconstructor, people.mozilla.org/~jorendorff/es6-draft.html#sec-getsuperconstructor
superConstructor is C.[GetPrototypeOf]
which should be
null
after the class definition, if I'm not wrong. (But it finally throws due to type error, so technically speaking, you can't even reference the super constructor)
No, as a special case of the extends
semantics, C.[[GetPrototypeOf]]()
will be %FunctionPrototype%; see step 6.e.ii of:
people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-classdefinitionevaluation
The reason is presumably that, since the constructor is a function, it should always have the methods for functions (.bind
, .call
, etc.) on its prototype chain.
The true meaning of C extends null
is the following: The instances of C
won’t have %ObjectPrototype% in their prototype chain. (For the use cases, don’t ask me.)
Ah, didn't notice the special case, thanks for the heads up.
Having methods for function in the prototype chain makes sense.
Another related question: If I want to override a superclass's constructor(), without calling it, I should do something like this?
class A extends B {
constructor() {
let _this = Object.create(new.target.prototype);
return _this;
}
}
Anyway to use this
here? Having to use a different name is a bit painful.
If I’m reading the latest spec draft correctly then
class C extends null { }
produces the following result:
C
:Function.prototype
C.prototype
:null
Neither #2 nor #3 seems very useful:
#2 means that a super-constructor call is allowed but throws an exception, because
Function.prototype
is not constructible. The default constructor will perform a super-constructor call. As a result, you are forced to explicitly return an object from the constructor if you don’t want an exception to be thrown.#3 means that the constructor doesn’t even create objects whose prototype is
null
, but objects whose prototype is an object whose prototype isnull
.Therefore my question: is this useful for anything? Can’t
extends null
be turned into something useful? Even treatingextends null
as equivalent to a missingextends
clause seems preferable.