W3C home > Mailing lists > Public > public-script-coord@w3.org > April to June 2013

Re: [Futures] accept/resolve/reject on a resolver don't have clearly defined behavior if no value is passed

From: Brendan Eich <brendan@secure.meer.net>
Date: Fri, 07 Jun 2013 12:35:42 +0100
Message-ID: <51B1C58E.7030300@secure.meer.net>
To: Boris Zbarsky <bzbarsky@MIT.EDU>
CC: Jonas Sicking <jonas@sicking.cc>, DOM public list <www-dom@w3.org>, Anne van Kesteren <annevk@annevk.nl>, Cameron McCormack <cam@mcc.id.au>, "public-script-coord@w3.org" <public-script-coord@w3.org>
Boris Zbarsky wrote:
> In ES, an argument is either "not passed" (as determined by 
> arguments.length) or has a value.  Arguments with a default value can 
> in fact be "not passed" in that default values do not affect the 
> arguments object at all.  For example:
>   function f(arg = 5) {
>     alert(arg + " " + arguments[0]);
>   }
>   f();
> alerts "5 undefined".  Furthermore, even when an argument was passed 
> its default value will still kick in if undefined is passed.
> I'd like to claim that the ES semantics here where default values 
> don't affect the arguments object are pretty weird and seem like a 
> footgun when combined with call/apply on the arguments object, but I 
> assume there's a reason for them...

A couple of reasons:

1. The arguments object should become legacy cruft with default and rest 
parameters, and the spread special form. These also take the heat off of 
apply (call is not relevant, right?).

That is, if you have ES3 or ES5 code of this form:

   function f(a, b) {
     a = a || 4;
     b = b || 5;
     return g.apply(undefined, arguments);

The ES6 way to write it is to avoid arguments altogether:

   function f(a = 4, b = 5, ...r) {
     return g(a, b, ...r);

If you needed apply for |this| forwarding, you'd either make a new 
array: g.apply(this, [a, b, ...r]) -- or else indeed use call: 
g.call(this, a, b, ...r).

2. Implementations reflect actual parameters as arguments, with missing 
actuals excluded. Default parameters are sugar (I'm simplifying how 
scoping works) for ||=. So

js> function f(a, b) { b = b || 42; 
print(Array.prototype.join.call(arguments)); }
js> f(1)
js> f(1,2)
js> f(1,2,3)

it doesn't matter that b has an old-school default.

> In any case, the question is how to reconcile the two sanely.  Once 
> that's done, I fully expect the simplicity of the C++ implementation 
> here to be nonexistent.

Ouch. But didn't TreatUndefinedAs in all its glory already do most of 
the damage?

Received on Friday, 7 June 2013 11:36:25 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 17:14:13 UTC