More fun with undefined

# T.J. Crowder (12 years ago)

Making a point of making this a separate thread from the current ?? and ??=thread(s), which are thankfully looking close to consensus. So that's infix and assignment.

Question: Should we consider unary as well?

I ask because I went searching through my code (and others') to see where I'd get a lot of use out of ?? and ??=, and I will, but I also found a lot of:

// 1 if (typeof foo === "undefined") { // Or foo === undefined // ... }

and

// 2 if (typeof foo !== "undefined") { // Or foo !== undefined // ... }

and

// 3 a = typeof foo !== "undefined" && foo;

...in my code, in jQuery, and in Prototype. jQuery also has a fair bit of this (which I find slightly odd, but...):

// 4 a = typeof foo !== "undefined" && foo(arg);

Given ?? and ??=, is there a call for some unary form addressing the above? Oddly, I think it would make the most sense if it were in the positive (true if the argument is undefined).

Just for illustration, I'll use £ as though it were an operator (because obviously it won't be that, and worrying about spelling is something we only need to do if the semantics are justified). The rule is simple: &evaluates true if its argument is undefined, false otherwise:

// 1 if (£foo) { // foo === undefined }

and

// 2 if (!£foo) { // foo !== undefined }

and

// 3 a = !£foo && foo; // a becomes foo if foo !== undefined // a becomes false if foo === undefined

-- T.J.

# T.J. Crowder (12 years ago)

On 14 June 2012 23:10, T.J. Crowder <tj at crowdersoftware.com> wrote:

The rule is simple: & evaluates true if its argument is undefined, false otherwise:

Slip of the fingers there. £, obviously. Not &. And again, the symbol is unimportant for now.

-- T.J.

# Allen Wirfs-Brock (12 years ago)

On Jun 14, 2012, at 3:10 PM, T.J. Crowder wrote:

Making a point of making this a separate thread from the current ?? and ??= thread(s), which are thankfully looking close to consensus. So that's infix and assignment.

Question: Should we consider unary as well?

I ask because I went searching through my code (and others') to see where I'd get a lot of use out of ?? and ??=, and I will, but I also found a lot of:

// 1 if (typeof foo === "undefined") { // Or foo === undefined // ... }

This is a different issue, but I wonder how badly the web would break if we made undefined a reserved word. Does anybody in JS really declare a different local binding for undefined? In ES5 we got away with making undefined read-only. Maybe we should continue pushing and see if we can eliminate the rebindable undefined hazard.

# Thaddee Tyl (12 years ago)

On Thu, Jun 14, 2012 at 3:29 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

This is a different issue, but I wonder how badly the web would break if we made undefined a reserved word.  Does anybody in JS really declare a different local binding for undefined?  In ES5 we got away with making undefined read-only.  Maybe we should continue pushing and see if we can eliminate the rebindable undefined hazard.

JQuery [1] famously has an "undefined" parameter, like so:

(function( window, undefined ) { … }(window))

What would happen in this case?

[1] code.jquery.com/jquery-1.7.2.js

# Tab Atkins Jr. (12 years ago)

On Thu, Jun 14, 2012 at 3:29 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

This is a different issue, but I wonder how badly the web would break if we made undefined a reserved word.  Does anybody in JS really declare a different local binding for undefined?  In ES5 we got away with making undefined read-only.  Maybe we should continue pushing and see if we can eliminate the rebindable undefined hazard.

There are definitely plenty of scripts on the web that declare undefined to shorten the scope look-up, either by using "var undefined;" or naming an unused argument "undefined". However, I would hope that the only cases that actually give it a new value are those that are being intentionally malicious a la evil.js.

# Rick Waldron (12 years ago)

On Thu, Jun 14, 2012 at 5:35 PM, Thaddee Tyl <thaddee.tyl at gmail.com> wrote:

On Thu, Jun 14, 2012 at 3:29 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

This is a different issue, but I wonder how badly the web would break if we made undefined a reserved word. Does anybody in JS really declare a different local binding for undefined? In ES5 we got away with making undefined read-only. Maybe we should continue pushing and see if we can eliminate the rebindable undefined hazard.

JQuery [1] famously has an "undefined" parameter, like so:

(function( window, undefined ) { … }(window))

Actually, this exists because undefined wasn't reserved. We would certainly remove the formal param in favor of an reserved undefined. Unfortunately, we can't "take it back" in extant code.

# Allen Wirfs-Brock (12 years ago)

On Jun 14, 2012, at 3:49 PM, Rick Waldron wrote:

On Thu, Jun 14, 2012 at 5:35 PM, Thaddee Tyl <thaddee.tyl at gmail.com> wrote: On Thu, Jun 14, 2012 at 3:29 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

This is a different issue, but I wonder how badly the web would break if we made undefined a reserved word. Does anybody in JS really declare a different local binding for undefined? In ES5 we got away with making undefined read-only. Maybe we should continue pushing and see if we can eliminate the rebindable undefined hazard.

JQuery [1] famously has an "undefined" parameter, like so:

(function( window, undefined ) { … }(window))

Actually, this exists because undefined wasn't reserved. We would certainly remove the formal param in favor of an reserved undefined. Unfortunately, we can't "take it back" in extant code.

A wonder if this wart is hairy enough, that we wouldn't be justified in some explicit backwards compatibility hackery in the spec. to remove it.

For example, we could allow it to appear in parameter lists and provide a dynamic check to ensure that nothing (other than a real undefined) is passed. Similarly we could explicitly allow: var undefined;

Certainly there is no particular reasons we need to allow: let undefined; const undefined=true; class undefined extends foo { } or any other new binding forms redefining undefined.

# Herby Vojčík (12 years ago)

T.J. Crowder wrote:

Making a point of making this a separate thread from the current ?? and ??= thread(s), which are thankfully looking close to consensus. So that's infix and assignment.

Question: Should we consider unary as well?

I also thought in these lines. What I came up is this:

(foo??) // (foo !== undefined) foo??bar // (foo !== undefined) ? foo : bar aka foo ?? foo : bar

that is, allow ?? also without the operand, but then only at the end of (sub)expression

it then allows |if (foo??)| or |let bar = foo??| (well, yes, ASI strikes back. again :-( I use semicolons always, but there's another school that doesn't)

-- T.J.

Herby

P.S.: foo??bar:baz wouldn't hurt either, to complete the triad.

# T.J. Crowder (12 years ago)

On 15 June 2012 07:42, Herby Vojčík <herby at mailbox.sk> wrote:

T.J. Crowder wrote:

Making a point of making this a separate thread from the current ?? and ??= thread(s), which are thankfully looking close to consensus. So that's infix and assignment.

Question: Should we consider unary as well?

I also thought in these lines. What I came up is this:

(foo??) // (foo !== undefined) foo??bar // (foo !== undefined) ? foo : bar aka foo ?? foo : bar

that is, allow ?? also without the operand, but then only at the end of (sub)expression

Again, let's consider whether the semantics are worth it before we get into synxtax. I take it you're in favor of something?

P.S.: foo??bar:baz wouldn't hurt either, to complete the triad.

I've suggested that a couple of times.[1][2] Brendan said he thought it was "too thin."[3] AFAIK no one else has weighed in on the subject.

[1] esdiscuss/2012-June/023356 [2] esdiscuss/2012-June/023465 [3] esdiscuss/2012-June/023468

-- T.J.

# Andreas Rossberg (12 years ago)

On 15 June 2012 01:22, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

A wonder if this wart is hairy enough, that we wouldn't be justified in some explicit backwards compatibility hackery in the spec. to remove it.

For example, we could allow it to appear in parameter lists and provide a dynamic check to ensure that nothing (other than a real undefined) is passed.  Similarly we could explicitly allow:       var undefined;

Actually, for very much the same effect, you could simply treat 'undefined' as a (refutable) pattern that is only matched by the undefined value. No need to make special rules for var or parameters then.

# Herby Vojčík (12 years ago)

T.J. Crowder wrote:

On 15 June 2012 07:42, Herby Vojčík <herby at mailbox.sk <mailto:herby at mailbox.sk>> wrote:

T.J. Crowder wrote:

    Making a point of making this a separate thread from the current
    ?? and
    ??= thread(s), which are thankfully looking close to consensus. So
    that's infix and assignment.

    Question: Should we consider unary as well?


I also thought in these lines. What I came up is this:

(foo??)         // (foo !== undefined)
foo??bar        // (foo !== undefined) ? foo : bar aka foo ?? foo : bar

that is, allow ?? also without the operand, but then only at the end
of (sub)expression

Again, let's consider whether the semantics are worth it before we get into synxtax. I take it you're in favor of something?

Well, now that I think about it... whatever. I can live with both. Though "trueish" !== undefined seems more natural to me (especially inside if).

If the new syntax is clear that it's otherwise, it can be === undefined, as well.

For example if (isnt foo) {...} (but this immediately suggest there can be unary is as well... which is nice, there could be both).

P.S.: foo??bar:baz wouldn't hurt either, to complete the triad.

I've suggested that a couple of times.[1][2] Brendan said he thought it was "too thin."[3] AFAIK no one else has weighed in on the subject.

I know, I just sort-of included it to show I like it.

# T.J. Crowder (12 years ago)

On 15 June 2012 08:09, Andreas Rossberg <rossberg at google.com> wrote:

On 15 June 2012 01:22, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

A wonder if this wart is hairy enough, that we wouldn't be justified in some explicit backwards compatibility hackery in the spec. to remove it.

For example, we could allow it to appear in parameter lists and provide a dynamic check to ensure that nothing (other than a real undefined) is passed. Similarly we could explicitly allow: var undefined;

Actually, for very much the same effect, you could simply treat 'undefined' as a (refutable) pattern that is only matched by the undefined value. No need to make special rules for var or parameters then.

Folks, could we move the unrelated discussion to its own thread? This thread's original subject is rather getting lost here.

-- T.J.

# Brendan Eich (12 years ago)

This isn't unrelated. If we do get patterns into Harmony they may subsume some of the postfix-?? (which does not work syntactically, IMHO) or prefix-? ideas. Or at least prefix-? may show up in the pattern language (dherman and I have discussed it).

Syntax design requries global oversight, there are cross-cutting concerns and different complexity budgets to bean-count (but never too locally or blindly).

# T.J. Crowder (12 years ago)

On 15 June 2012 12:41, Brendan Eich <brendan at mozilla.com> wrote:

This isn't unrelated. If we do get patterns into Harmony they may subsume some of the postfix-?? (which does not work syntactically, IMHO) or prefix-? ideas. Or at least prefix-? may show up in the pattern language (dherman and I have discussed it).

I was talking about the discussion around making undefined non-redefinable. Of the first 6 responses, one was about the original subject (having a unary version of ??), the others were about whether undefined should be redefinable. Different (and potentially useful) topic, in my view. Allen even opened with "This is a different issue, but..."

-- T.J.

# Aymeric Vitte (12 years ago)

Probably there are very good reasons but I reask the question : why should the attempt to access a property of a variable equal to undefined global property not return undefined global property ? (ie a way that this works : if (a.b.c.d) {} when a,a.b, etc are not set)

This can be usefull when things get loaded asynchronously and then when you don't know when it will be available (it does not happen every day, but in some cases you don't have necessarily a callback or an event to tell you and then asynchronous stuff can start being extremely difficult to handle).

Example :

console.log(a);//Reference error, GetBase returns undefined

console.log(window.a);//undefined

--> does not seem very logical, no ?

Maybe it was discussed thousand of times, but why not :

8.9.1 GetValue (V) ... 5. If IsUnresolvableReference(V), return undefined

Le 15/06/2012 01:22, Allen Wirfs-Brock a écrit :

# T.J. Crowder (12 years ago)

On 15 June 2012 14:34, Aymeric Vitte <vitteaymeric at gmail.com> wrote:

Example :

console.log(a);//Reference error, GetBase returns undefined

console.log(window.a);//undefined

--> does not seem very logical, no ?

To me this would be a big step backward, after the very large stride forward this group made in ES's strict mode of making assigning to an unresolvable reference an error rather than an implicit creation of a property on the global object.

For one thing, how is the engine to know that the a in question was meant to be window.a? Maybe I just forgot to put var a in the current scope. (In fact, that's usually what it is when I get this error.)

Scope is not the same as an object (although of course, the scope chain is conceptually made up of binding objects). If I refer to a in my code and a has never been declared, that's a bug, and as I haven't told it, the engine has no way of knowing what level in the scope chain I intended a to be in. If I access the a property of an object, and the property has never been defined, that could just be lazy initialization; the engine knows that I'm talking about that specific object (or its prototypes), because I've told it what object to look at.

Separately:

(ie a way that this works : if (a.b.c.d) {} when a,a.b, etc are not set)

Making the initial a evaluate to undefined wouldn't make that work: Instead of the ReferenceError, you'd get a TypeError (cannot read property b of undefined).

-- T.J.

# Aymeric Vitte (12 years ago)

I am not talking about defining implicit properties or such things, neither having undeclared stuff looking declared, but just changing the behavior of retrieving a property when base is undefined, which will then be undefined.

If am I reading correctly the specs, doing this change will work for a.b.c.d, because undefined is returned first and nothing is set anywhere, unless I am wrong :

console.log(a); //reference error

var a;//undefined console.log(a.b);//reference error

will become :

console.log(a); //undefined

var a;//undefined console.log(a.b);//undefined

Le 15/06/2012 15:52, T.J. Crowder a écrit :

# T.J. Crowder (12 years ago)

On 15 June 2012 15:34, Aymeric Vitte <vitteaymeric at gmail.com> wrote:

I am not talking about defining implicit properties or such things, neither having undeclared stuff looking declared, but just changing the behavior of retrieving a property when base is undefined, which will then be undefined.

If am I reading correctly the specs, doing this change will work for a.b.c.d, because undefined is returned first and nothing is set anywhere, unless I am wrong :

console.log(a); //reference error

var a;//undefined console.log(a.b);//reference error

will become :

console.log(a); //undefined

var a;//undefined console.log(a.b);//undefined

So you're proposing two changes:

  1. Let getting an unresolvable symbol evaluate to undefined, rather than causing a ReferenceError (which requires changing GetValue, at least), and

  2. Allow accessing a property of undefined to evaluate to undefined, rather than causing a TypeError (which requires changing either how property accessors work, or changing the definition of CheckObjectCoercible so that it coerces undefined into {} or some such).

You'd need both of them to make a.b "work" (evaluate to undefined rather than throwing) where a is unresolvable, #1 to prevent the ReferenceError on a, and #2 to prevent the TypeError when trying to retrieve b from undefined.

Either would be a big, breaking change on its own.

Re #1: I wouldn't be in favor, FWIW. I think it's much better to have a ReferenceError on an unresolvable symbol than have it treated as though it were resolved, but had the value undefined. Again, the feature of strict mode where assigning to unresolvable symbols stopped creating global variables was a Good Thing(tm). :-) To me this is in the same vein as the old behavior there was. If I try to read the value of an unresolvable symbol, I want the proactive notification I get from the ReferenceError; I don't want to try to find the subtle bug the implicit undefined helps hide (in my view).

Re #2: I wouldn't be in favor, FWIW. Consider:

function foo(o) { console.log(o.msg); } foo();

Currently, the call to foo throws, because I'm not passing in any argument, but the code dereferences o without testing first. This is clearly a bug in the call to foo. Currently, I get proactive notification of that bug (via the TypeError). With change #2, the code would try to log undefined instead -- a much harder, more subtle bug to try to find and fix.

Yes, the current behavior wrt reading from unresolvable references vs. reading from undefined properties can be viewed, from some angles, as inconsistent. But it's very thoroughly ingrained in the language, and I don't see a strong argument for change.

Just my $0.02.

Best,

-- T.J.

# Aymeric Vitte (12 years ago)

Right now I am not proposing but trying to understand why it is like this (and why on some aspects it is strange), and if by any chance a.b.c.d could be solved. Indeed this would require an early return from Accessors too after GetValue(base) . I don't understand why you focus on global var being created, this is not the case.

Maybe I am misreading something in the chain, the Accessors CheckObjectCoercible and GetValue IsUnresolvableReference look to be redundant in this case.

var a; console.log(a.b); //TypeError for you (correct ?)- Reference error for me console.log(c.d);//Reference error for me

Le 15/06/2012 16:56, T.J. Crowder a écrit :

# T.J. Crowder (12 years ago)

On 15 June 2012 17:00, Aymeric Vitte <vitteaymeric at gmail.com> wrote:

Right now I am not proposing but trying to understand why it is like this

Sorry, I thought you were proposing something. Your first message talked about changing how GetValue works, apologies if I misunderstood.

Brendan could speak FAR better to "why" than I can. I would expect it's because, as I said earlier, scope and identifier resolution is just plain different from objects and property resolution. Maybe they didn't have to be different, but they are, and always has been. Attempts to conflate the two (the with structure, for instance, which intermixes an object's properties with scope resolution) have been unsuccessful and are now seen to be...not the best way forward.

...and if by any chance a.b.c.d could be solved

If you want to get a.b.c.d but you aren't sure whether a is resolvable, you can do this:

var v = typeof a === "object" && typeof a.b === "object" && typeof a.b.c === "object" && a.b.c.d;

v will end up with either undefined or the value of a.b.c.d. Taking the type of an unresolvable identifier doesn't cause a ReferenceError.

Indeed this would require an early return from Accessors too after GetValue(base) . I don't understand why you focus on global var being created, this is not the case.

I wasn't trying to say your change to GetValue resulted in creating globals. I'm just pointing out the similarity of issues between your proposed behavior (using an implicit undefined when faced with getting an unresolvable identifier) and that previous behavior (creating a global when putting to an unresolvable identifier). The previous behavior was sufficiently problematic that ES moved away from doing this sort of implicit stuff toward using ReferenceError more consistently (e.g., on put as well as get, in strict mode).

Maybe I am misreading something in the chain, the Accessors CheckObjectCoercible and GetValue IsUnresolvableReference look to be redundant in this case.

var a; console.log(a.b); //TypeError for you (correct ?)- Reference error for me console.log(c.d);//Reference error for me

With the var a; there, my understanding is console.log(a.b) should raise a TypeError, and that's what I get from V8.[1] (Be sure you have the console open when opening that.) Without the var a; declaration, it should raise a ReferenceError. Why:

With the var a; there, a is undefined (not unresolvable), so for the a.b expression, the property accessor operation[2] gets the base (steps 1 and 2), which will be undefined, and the property name (steps 3 and 4), which will be "b". Step 5 is to call CheckObjectCoercible[3] passing in the base, which is undefined. CheckObjectCoercible throws a TypeError if given undefined.

Without the var a;, a is unresolvable and so before we even get to the property accessor, we've already run into the ReferenceError from GetValue (which you highlighted in your original message).

Regardless of why, I for one am happy that reading from (and now in strict mode writing to) unresolvable identifiers results in a ReferenceError. I get nice proactive notification when I forget to declare things. :-)

[1] jsbin.com/ufezog [2] ecma-international.org/ecma-262/5.1/#sec-11.2.1 [3] ecma-international.org/ecma-262/5.1/#sec-9.10

-- T.J.