A new ES6 draft is available
On 28 September 2013 00:05, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
This is Rev 19, The Sept. 27 Draft harmony:specification_drafts#september_27_2013_draft_rev_19 And the HTML is available at people.mozilla.org/~jorendorff/es6-draft.html
Changes include: [...]
Made Symbol a primitive type with a wrapper object named Symbol Make new Object(Symbol()) throw a TypeError
I can't remember any discussion about Object(Symbol()). Why should it be disallowed?
On the other hand, we agreed the other week on making 'new Symbol' and Symbol.prototype.toString throw. That should address all likely accidents.
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of
I can't remember any discussion about Object(Symbol()). Why should it be disallowed?
On the other hand, we agreed the other week on making 'new Symbol' and Symbol.prototype.toString throw. That should address all likely accidents.
I only remember Dave briefly mentioning it as one of the potential ways to get a wrapper object; I don't remember a decision to prohibit it.
On the other hand, this is somewhat attractive, as it prevents getting a symbol wrapper object except inside sloppy-mode methods/accessors added to Symbol.prototype
, wherein this
is a wrapper. Which means, unless I missed a case, that in strict mode it is impossible to create a symbol wrapper object.
On 30 September 2013 13:41, Domenic Denicola <domenic at domenicdenicola.com> wrote:
I only remember Dave briefly mentioning it as one of the potential ways to get a wrapper object; I don't remember a decision to prohibit it.
On the other hand, this is somewhat attractive, as it prevents getting a symbol wrapper object except inside sloppy-mode methods/accessors added to
Symbol.prototype
, whereinthis
is a wrapper. Which means, unless I missed a case, that in strict mode it is impossible to create a symbol wrapper object.
Yes, but there seems nothing inherently risky or error-prone in (purposefully and explicitly) creating a wrapper object. And since you can do it for other primitive types, I think the scales weigh towards consistency on this one.
On Sep 30, 2013, at 4:53 AM, Andreas Rossberg wrote:
Yes, but there seems nothing inherently risky or error-prone in (purposefully and explicitly) creating a wrapper object. And since you can do it for other primitive types, I think the scales weigh towards consistency on this one.
At this point, we should probably discuss what is actually in the spec. draft rather than the change summary or the meeting notes.
The lastest draft throws when doing an implicitly ToString conversion 1 of a Symbol primitive value. This means that (aSymbol + "suffix") or ("prefix" + aSymbol) 2 will throw, which was the case that I believe people were most concerned about.
ToNumber conversion of a Symbol value is currently specified 3 as producing a NaN for implicit numeric conversions of Symbols resulting in NaN values that will propagate through numeric computations. We could make ToNumber throw for Symbol values but NaN seems more consistent with the ES handling of invalid numeric conversions.
ToBoolean of a String value is defined3 to return true. This seems consistent with the rest of ES, although it would be easy enough to define this conversion to throw.
This isn't current in the spec, but we should probably also make implicit string or number conversions of Symbol wrappers throw. We can to this simply by defining a @@ToPrimitive method 4 on Symbol.prototype that throws .
Because, all of the appropriate implicit conversions produce error results, I didn't see any real reason why an explicit call to String.prototype.toString should throw. This certainly seems like a reasonable way for a program to explicitly convert a Symbol value to a string value for debugging, logging, or other proposes. This method is currently defined 5 to produce a string of the form "Symbol(<descriptive string provided when created>)". (modulo a small spec. bug)
As discussed at the meeting, (new Symbol) will throw. This is accomplished by making Symbol's @@create method 6 throw.
Object(aSymbol) is currently spec'ed to return a wrapper for the Symbol via a call to ToObject 7. While idea of throwing was mentioned at the meeting, I'm not sure why it should be special cased to throw. This is how a programmer would generically convert primitive values to objects in situations where this is necessary. For example,
"toString" in Object(expr)
Note that a call to ToObject is also how the implicit conversions used to deference Reference values with primitive value bases (eg, aSymbol.foo) works.
I'm not sure why my change log says (new Object(Symbol())) throws because that isn't actually in the draft 9. Perhaps I was about to make that change, based upon the meeting minutes, and got interrupted or something. Regardless, it does need to do something. The most consistent thing would be to treat it, like all other primitive values, as a request to create a Symbol wrapper object.
Allen
On 9/30/2013 4:41 AM, Domenic Denicola wrote:
I can't remember any discussion about Object(Symbol()). Why should it be disallowed?
On the other hand, we agreed the other week on making 'new Symbol' and Symbol.prototype.toString throw. That should address all likely accidents. I only remember Dave briefly mentioning it as one of the potential ways to get a wrapper object; I don't remember a decision to prohibit it.
On the other hand, this is somewhat attractive, as it prevents getting a symbol wrapper object except inside sloppy-mode methods/accessors added to
Symbol.prototype
, whereinthis
is a wrapper. Which means, unless I missed a case, that in strict mode it is impossible to create a symbol wrapper object.
Calling Object as a function is currently infallible and I've seen plenty of code that makes this assumption. This should probably not be changed.
You shoot, you score.
This is decisive -- cc'ing Allen.
On Sep 30, 2013, at 1:14 PM, Brendan Eich wrote:
You shoot, you score.
This is decisive -- cc'ing Allen.
Already spec'ed to do the wrapping. See my previous message on this thread.
On 30 September 2013 18:20, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
The lastest draft throws when doing an implicitly ToString conversion [1] of a Symbol primitive value. This means that (aSymbol + "suffix") or ("prefix" + aSymbol) [2] will throw, which was the case that I believe people were most concerned about.
ToNumber conversion of a Symbol value is currently specified [3] as producing a NaN for implicit numeric conversions of Symbols resulting in NaN values that will propagate through numeric computations. We could make ToNumber throw for Symbol values but NaN seems more consistent with the ES handling of invalid numeric conversions.
ToBoolean of a String value is defined[3] to return true. This seems consistent with the rest of ES, although it would be easy enough to define this conversion to throw.
This isn't current in the spec, but we should probably also make implicit string or number conversions of Symbol wrappers throw. We can to this simply by defining a @@ToPrimitive method [4] on Symbol.prototype that throws .
OK, thanks for the summary, that makes sense. The one worry I have, though, is that not tainting S.p.toString itself will miss cases where code tries to convert some value x to a property name by explicitly calling x.toString(). Currently, that works for everything but null and undefined, so I assume that this pattern is used quite a bit.
Because, all of the appropriate implicit conversions produce error results, I didn't see any real reason why an explicit call to String.prototype.toString should throw. This certainly seems like a reasonable way for a program to explicitly convert a Symbol value to a string value for debugging, logging, or other proposes. This method is currently defined [5] to produce a string of the form "Symbol(<descriptive string provided when created>)". (modulo a small spec. bug)
There always is the generic Object.prototype.toString that can be made to handle symbols. That is what V8 currently does (producing a string just like you describe above).
On Oct 1, 2013, at 3:10 AM, Andreas Rossberg wrote:
OK, thanks for the summary, that makes sense. The one worry I have, though, is that not tainting S.p.toString itself will miss cases where code tries to convert some value x to a property name by explicitly calling x.toString(). Currently, that works for everything but null and undefined, so I assume that this pattern is used quite a bit.
Do you think thing it really is? Any value (other than null and undefined) automatically converts to a string when used as a property selector. so there really isn't any reason (other than lack of knowledge) for someone to explicitly toString values before using them as a property key. On the other hand, there are plenty of reasons in other use cases where someone might explicitly toString because they actually want a string description of a value.
Either choice we make here is probably going to trip up some code. I think what I've specified is the best set of trade-offs, but I willing to be convinced otherwise. I suspect hard evidence supporting either alternative is hard to find
There always is the generic Object.prototype.toString that can be made to handle symbols. That is what V8 currently does (producing a string just like you describe above).
But this forces such code to be rewritten to special case Symbol values (assuming that they don't want, for example, string values and Arrays to display as [object. String] and [object Array]). Also, the spec. current says that O.P.toString for a Symbol returns [object Symbol] rather than Symbol(<descriptive string provided when created>). This is to keep it consistent with other types of values and code that expects O.p.toString to always produce the [object <name>] pattern.
Allen Wirfs-Brock wrote:
Currently, that works for everything but null and undefined, so I assume that this pattern is used quite a bit.
Do you think it really is?
I don't. (That is, I don't see much x.toString() feeding into bracketed property lookup, if any.)
Explicit .toString() calling is rare because verbose, in addition to throwing on null and undefined. People use + ''. But that's covered, and I say if someone has a symbol named x (whether they know it's a symbol or "any") and they call .toString(), they should get what they're asking for: throw on null or undefined (or a misbehaving impl), otherwise a string.
Le 1 oct. 2013 à 19:33, Brendan Eich <brendan at mozilla.com> a écrit :
Allen Wirfs-Brock wrote:
Currently, that works for everything but null
and undefined, so I assume that this pattern is used quite a bit.
Do you think it really is?
I don't. (That is, I don't see much x.toString() feeding into bracketed property lookup, if any.)
Explicit .toString() calling is rare because verbose, in addition to throwing on null and undefined. People use + ''.
I tend to use String(x)
(which is equivalent).
But that's covered, and I say if someone has a symbol named x (whether they know it's a symbol or "any") and they call .toString(), they should get what they're asking for: throw on null or undefined (or a misbehaving impl), otherwise a string.
A bit annoying that ad-hoc debugging code like alert("got argument: " + x)
or alert("got argument: " + String(x))
will throw for Symbols, while alert("got argument: " + x.toString())
will throw for null and undefined, and (usually) for Object.create(null).
On Oct 1, 2013, at 1:22 PM, Claude Pache wrote:
Le 1 oct. 2013 à 19:33, Brendan Eich <brendan at mozilla.com> a écrit :
Allen Wirfs-Brock wrote:
Currently, that works for everything but null
and undefined, so I assume that this pattern is used quite a bit.
Do you think it really is?
I don't. (That is, I don't see much x.toString() feeding into bracketed property lookup, if any.)
Explicit .toString() calling is rare because verbose, in addition to throwing on null and undefined. People use + ''.
I tend to use
String(x)
(which is equivalent).
Actually it isn't. String(x) is specified to call the ToString abstract operation on x which in the current spec./proposal will throw if x is a Symbol value.
But that's covered, and I say if someone has a symbol named x (whether they know it's a symbol or "any") and they call .toString(), they should get what they're asking for: throw on null or undefined (or a misbehaving impl), otherwise a string.
A bit annoying that ad-hoc debugging code like
alert("got argument: " + x)
oralert("got argument: " + String(x))
will throw for Symbols, whilealert("got argument: " + x.toString())
will throw for null and undefined, and (usually) for Object.create(null).
I suppose we could special case String(x) when x is a Symbols
Explicit .toString() calling is rare because verbose, in addition to throwing on null and undefined. People use + ''.
I tend to use
String(x)
(which is equivalent).Actually it isn't. String(x) is specified to call the ToString abstract operation on x which in the current spec./proposal will throw if x is a Symbol value.
Sorry for for the ambiguity, I meant: equivalent to + ''
.
Rev31 (January 15, 2015) of the ECMAScript 2015 Language Specification is ready for review at harmony:specification_drafts#january_15_2015_draft_rev_31
Changes include: Updated specification to use and support the new built-in subclassing scheme described at: tc39/ecma262/blob/master/workingdocs/ES6-super-construct%3Dproposal.md
- Added newTarget parameter to [[Construct]], Reflect.construct, and proxy ‘construct’ trap.
- Added NewTarget binding to Function Environment Records and abstract operations/methods of setting and accessing that binding
- Redefined [[Construct]] for ordinary ECMAScript functions so “derived” constructors don’t preallocate the new object and bind this.
- Changed [[Construct]] for ordinary ECMAScript functions so “derived” constructors will throw if they try to explicitly return a non-object.
- Within constructors this binding has TDZ access semantics.
- super(...) syntactic form now illegal in Function/GeneratorDeclaration/Expression
- super(...) and new super(...) propagates current NewTarget to [[Construct]] call
- super(...) binds this value upon return from [[Construct]] call, throw if new already bound
- Refactored [[Call]] and [[Construct]] for ordinary ECMAScript functions so they can continue to shared common spec steps.
- Updated Generator object instantiation to work with new [[Construct]] design
- Refactored Function constructor and GeneratorFunction to share a common abstract operation based definition
- Every built-in constructor changed to merge object allocation and initialization code. In some cases (eg, TypedArray and Promise) significant refactoring of allocation and initialization logic.
- Updated [[Construct]] of Bound functions to handle newTarget parameter.
- Several of the above items are tentative because they still lack full TC39 discussion and review. The following are particularly tentative:
- restrictions on where super(...) is allowed may change
- new supper(...) might go away or change
- Support for the new.target access syntax (yellow highlight in spec text) is speculative and may move to ES7 and/or change
Other changes
- Merged AllocArrayBuffer and SetArrayBufferData into single abstract operation
- %TypedArray%.of now requires that its this value is a valid Typed Array constructor
- Replaced “moduleId” with “sourceCodeId” and eliminated most usage of such ids in module related abstract operations
- Some tweaking of Language Overview in 4.2
- A new document title
- Resolved bugs: 3543-3527, 3524-3523, 3520, 3517-3516, 3514-3511, 3501-3496, 3494-3479, 3310, 3229, 3136, 2865, 2536, 2495, 2179
Changes include: Updated specification to use and support the new built-in subclassing scheme described at: tc39/ecma262/blob/master/workingdocs/ES6-super-construct%3Dproposal.md
This looks nice. An interesting question:
For classes that have divergent [[Construct]]/[[Call]] behavior, should the [[Call]] behavior be "inherited" by derived classes?
class MyDate extends Date {}
console.log(MyDate(1, 2, 3)); A string, or throw an error?
Since the above class definition does not include an explicit constructor body, it gets the equivalent of
constructor(...args) {super(...args)}
as its implicit constructor definition
A 'super()' call throws if the NewTarget is null (ie, if the constructor is invoked via [[Call]] )
If you want to inherit call behavior you need to code it as:
class MyDate extends Date {
constructor(...args) {
if (new.target===null) return super.constructor(...args)
super(...args);
}
}
That's assuming 'new.target' makes it into ES6. Without it you would have to do something like:
class MyDate extends Date {
constructor(...args) {
let calledAsFunction=true;
try {
let thisValue = this; //can't reference 'this' prior to 'super()' in a [[Construct]] call of a derived function
} catch (e} {
let calledAsFunction = false
}
if (calledAsFunction) return super.constructor(...args)
super(...args);
}
}
err,
try {
let thisValue = this; //can't reference 'this' prior to 'super()' in a [[Construct]] call of a derived function
} catch (e} {
calledAsFunction = false //no let here
}
Gotta agree new.target is nicer to read and write!
I'm also expecting a lot of questions about 'what is this new.target thing in this code?', 'is new a variable?', 'where does it come from?', 'isn't new an operator?', etc.
if (this instanceof MyDate) ...
... is clearer, but I guess it needs to be disallowed because of the other rules.
On Jan 17, 2015, at 5:59 AM, Frankie Bagnardi wrote:
I'm also expecting a lot of questions about 'what is this new.target thing in this code?', 'is new a variable?', 'where does it come from?', 'isn't new an operator?', etc.
as proposed, new.target
is a MemberExpression consisting of the three token sequence new
.
target
It is only allowed in function code.
If the enclosing function is invoked as a call expression the value of new.target
is null
(function() {assert(new.target===null)})()
if the enclosing function is directly invoked as a constructor via new
the value of new.target
is the function
function f() {assert(new.target===f)}
new f();
if the enclosing function is indirectly invoked as a constructor via super()
or new super()
the value of new.target
is the function new was directly applied to
class Super { constructor() {assert(new.target===Sub)}}
class Sub extends Super {constructor() {super()}}
new Sub();
if (this instanceof MyDate) ...
... is clearer, but I guess it needs to be disallowed because of the other rules.
When a function is called (rather than new'ed) its this
value is usually undefined (assuming a strict function)
Le 17 janv. 2015 à 14:59, Frankie Bagnardi <f.bagnardi at gmail.com> a écrit :
I'm also expecting a lot of questions about 'what is this new.target thing in this code?', 'is new a variable?', 'where does it come from?', 'isn't new an operator?', etc.
if (this instanceof MyDate) ...
... is clearer, but I guess it needs to be disallowed because of the other rules.
I don't find the this instanceof MyDate
test clearer, but it may appear so because it is idiomatic. In fact, it is a somewhat indirect way to test what you want. (And it may be incorrect in some cases, which is a danger when you use indirect paths.)
On Jan 17, 2015, at 12:31, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
If the enclosing function is invoked as a call expression the value of
new.target
is null
Just curious, why null instead of undefined?
Frankie Bagnardi wrote:
I'm also expecting a lot of questions about 'what is this new.target thing in this code?', 'is new a variable?', 'where does it come from?', 'isn't new an operator?', etc.
if (this instanceof MyDate) ...
... is clearer, but I guess it needs to be disallowed because of the other rules.
It doesn't mean the same thing as new.target, of course. Also, new.target is not useful only as a predicate -- it is the original constructor that was new'ed.
On Jan 17, 2015, at 9:53 AM, Domenic Denicola wrote:
Just curious, why null instead of undefined?
null is used to indicate no [[Prototype]], so it seem to me to be a better match for this situation.
If practice, I suspect that a truthy test will usually be applied, so it doesn't make much difference.
I agree with Frankie. Assume a developer who has never seen this
new.target
construct before.
They will first think that this is an invalid expression, as new
is an
operator. Then, upon seeing this code execute, the natural question is
"What is new
? Is it an identifier injected into Environment Records
created by [[Call]] and [[Construct]]? Does this identifier resolve to an
object (so that the MemberExpression would make sense)?"]
Fabrício Matté wrote:
I agree with Frankie. Assume a developer who has never seen this
new.target
construct before.
In general, ES6 has new syntax, so this is a "learn it and use it" bump, one of many.
They will first think that this is an invalid expression, as
new
is an operator. Then, upon seeing this code execute, the natural question is "What isnew
? Is it an identifier injected into Environment Records created by [[Call]] and [[Construct]]? Does this identifier resolve to an object (so that the MemberExpression would make sense)?"]
new.target in the proposal (which does not yet have consensus) evaluates to an object, but (new) is a syntax error as in ES1-5.
Yeah, it's a bit odd to grab new.identifier syntax-space and start by defining only new.target. Alternatives welcome. new^ already got an anti-grawlix reaction, new? did too (perhaps less but still).
For a short term solution, I would suggest arguments.new.target
.
The arguments
object already contains info about how the function was
called (its arguments, the deprecated/obsolete callee
and
obsolete/never-spec'd caller
properties), so adding information about how
the function was called (whether it was new
'ed) to arguments
would make
sense imo.
For the long term, I'd like to see a new identifier injected into function
scopes which exposes the Lexical Environment/Environment Record internals.
Then we can use __scope__.new.target
or Reflect.isNewed(__scope__)
(or
isConstructed
, which may make more sense seeing as there will be
Reflect.construct
).
Of course, this __scope__
binding should only be injected in the
Environment Record if the binding does not exist yet after registering the
function body's declarations, for back-compat reasons.
And obviously, __scope
is just a placeholder name for this suggestion, I
don't really mind how it will be called.
On Jan 17, 2015, at 10:50 AM, Brendan Eich wrote:
Fabrício Matté wrote:
I agree with Frankie. Assume a developer who has never seen this
new.target
construct before.In general, ES6 has new syntax, so this is a "learn it and use it" bump, one of many.
A general challenge in extending JS is that we can't add new reserved words, so adding new meaning to existing reserved keywords in one way around that limitation.
Currently in ES6, the only reserved keywords that can appear immediately before a .
are this
and super
. Any other reserved word followed by a .
is a syntax error. So reserved words followed by period and an identifier is one of the few available extension alternatives we have available. And is a natural ready syntax think of new.target' as meaning give me the target value of the currently active new
operator.
They will first think that this is an invalid expression, as
new
is an operator. Then, upon seeing this code execute, the natural question is "What isnew
?
hopefully they will say what is `new.target', "google it" and immediately find the answer.
Is it an identifier injected into Environment Records created by [[Call]] and [[Construct]]? Does this identifier resolve to an object (so that the MemberExpression would make sense)?"]
I suspect the hypothetical naive JS programmer postulated above wound't be aware of any of those concepts. That's why I explained in down thread without using them.
new.target in the proposal (which does not yet have consensus) evaluates to an object, but (new) is a syntax error as in ES1-5.
Yeah, it's a bit odd to grab new.identifier syntax-space and start by defining only new.target. Alternatives welcome. new^ already got an anti-grawlix reaction, new? did too (perhaps less but still).
Once this idea is in play, It also seems like a good solution for some other extensions we have grapple with. Consider, for example:
function.callee
function.arguments
module.name
etc.
This should be an interesting area of exploration for ES7 and beyond
Once this idea is in play, It also seems like a good solution for some other extensions we have grapple with. Consider, for example:
function.callee function.arguments module.name
or yield.input
arguments.new.target makes more sense to me.
If we do want to move towards keyword.identifier being a normal occurance, then I think new.target is perfectly reasonable. I'm all for that, and it makes the job of tooling a lot simpler where it's used.
In the current draft, is new["target"] a syntax error, or equivalent to new.target?
On Jan 17, 2015, at 11:37 AM, Frankie Bagnardi wrote:
arguments.new.target makes more sense to me.
the problem with this or hanging anything off of arguments
is that it can be a potential accidental capability leak if people pass arguments
around thinking they are just passing an array of values. That's why ES5 removed arguments.callee
from strict mode.
If we do want to move towards keyword.identifier being a normal occurance, then I think new.target is perfectly reasonable. I'm all for that, and it makes the job of tooling a lot simpler where it's used.
In the current draft, is new["target"] a syntax error, or equivalent to new.target?
syntax error! new.target
is a special form and not a property access. We probably don't want to allow make it seem more like a property access by allowing a [ ]
formulation.
Currently in ES6, the only reserved keywords that can appear immediately before a
.
arethis
andsuper
.
The this
binding resolves to a value, so MemberExpressions make sense.
The super
keyword is being implemented in ES6, so there are no precedents
to set expectations.
Any other reserved word followed by a
.
is a syntax error. So reserved
words followed by period and an identifier is one of the few available
extension alternatives we have available. And is a natural ready syntax
think of new.target' as meaning give me the target value of the currently
active new
operator.
I agree new.target
is very natural and pleasant to read. It just feels
rather alien to see an operator in the beginning of a MemberExpression,
which currently would only be allowed in this very specific scenario. Of
course, if this syntax extension form would be useful for other use cases
as Kevin and you have outlined, then I don't oppose it.
I suspect the hypothetical naive JS programmer postulated above wound't be aware of any of those concepts.
A naive one probably not, but a curious avid developer most definitely would. ;)
hopefully they will say what is `new.target', "google it" and immediately find the answer.
You mean, land on a Stack Overflow answer with thousands of upvotes and
very little explanation about why/how a MemberExpression can begin with an
operator. Of course, if you interpret new
as simply a ReservedWord token
instead of an operator, then everything makes perfect sense.
On Jan 17, 2015, at 11:57 AM, Fabrício Matté wrote:
You mean, land on a Stack Overflow answer with thousands of upvotes and very little explanation about why/how a MemberExpression can begin with an operator. Of course, if you interpret
new
as simply a ReservedWord token instead of an operator, then everything makes perfect sense.
The way I accomplish in the grammar was to add the productions:
MemberExpression :
MetaProperty
MetaProperty :
'new' '.' 'target'
So we could start talking about "meta properties" as a MemberExpression alternative and say new.target
is a "meta property".
Oh thanks! I just got a copy of the draft rev 31 to check it out.
"Meta properties" sounds like a good name to me. Also, "JavaScript meta properties" does not have significantly relevant search results, so it should be easy to google for in the future.
On 17 January 2015 at 19:14, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
null is used to indicate no [[Prototype]], so it seem to me to be a better match for this situation.
Wouldn't the fact that null is a quasi-legal prototype strongly speak for using undefined here? Otherwise, it seems you couldn't distinguish Call invocations from Construct invocations with a prototype that has actually been set to null (which I suppose is legal?).
(In terms of proper option/maybe types, this is yet another case of a None vs Some(None) distinction.)
new.target
is a reference to the constructor, not the prototype, so the problem does not arise in practice.
But anyhow, I do think that undefined
is semantically better here:
new.target === null
means:new.target
has been set to "no object".new.target === undefined
means:new.target
has not been set.
When you execute a function body with the semantics of [[Construct]], the value of new.target
is the original constructor on which new
was applied. If it was possible to have the semantics of [[Construct]] with no original constructor, then new.target
would be null
(no-object).
But when you execute a function body with the semantics of [[Call]], there is no notion of "original constructor", and new.target
is left with no value, i.e. undefined
.
On Jan 19, 2015, at 5:51 AM, Claude Pache wrote:
But anyhow, I do think that
undefined
is semantically better here:
new.target === null
means:new.target
has been set to "no object".new.target === undefined
means:new.target
has not been set.
At the JS level, I don't actually think about new.target
as something that is "settable". I think about it as an oracle that tells me about how this function was invoked. null means it was invoked "as a function". non-null means it was invoked as a constructor and the value is the object that new
was applied to.
When you execute a function body with the semantics of [[Construct]], the value of
new.target
is the original constructor on whichnew
was applied. If it was possible to have the semantics of [[Construct]] with no original constructor, thennew.target
would benull
(no-object).
But it isn't. Reflect.construct ensures that an non-null value is passed to [[Construct]] as its second argument
But when you execute a function body with the semantics of [[Call]], there is no notion of "original constructor", and
new.target
is left with no value, i.e.undefined
.
I originally intended new.target
to use undefined
is the sentinel value to indicated "called as a function". But as I wrote the spec. it felt better to use null
in that role. I think it's because using null
seems more special. undefined
is used in so many places to indicated so many different things that it is hard to apply any generalized meaning to it. On the other hand, null
is used in only a few places in the ES spec. so its use seems to draw attention to the specialness of those situations.
But, I'd have no problem with changing back to undefined
if there is a consensus in favor of that.
It really makes very little difference as null
and undefined
are both falsey values, so the preferred way to write a "called as a function" these should probably be:
if (! new.target) ...
Maybe this is wrong, but I'm in the habit of thinking that if something has a value of null, something in JavaScript code (mine or a library I'm using) explicitly set it to null. If something has a value of undefined, it was never set. For that reason, I'd prefer using undefined in this case over null.
Okay. Thanks.
This is Rev 19, The Sept. 27 Draft harmony:specification_drafts#september_27_2013_draft_rev_19 And the HTML is available at people.mozilla.org/~jorendorff/es6-draft.html
Changes include: