block scope and function declarations
On Wed, 2012-02-15 at 12:55 +0100, Andy Wingo wrote:
function f(x) { bar(); // ??? for (let y of x) { function bar() { ... y ... } bar(); // OK } bar(); // ??? }
Did I just get wtfjs'd here? Is this `bar' not a source element, and therefore this is a function expression and not a declaration?
Sheepishly yours,
Andy
On Feb 15, 2012, at 3:55 AM, Andy Wingo wrote:
Hello again ecmascriptians,
There has been some discussion about treating function declarations as "var" declarations. While I understand the concerns about compatibility, I think it will be surprising to programmers.
Not really, the intent is that function declarations are lexically scoped to the closest inclosing block (or function body), just like let. The primary difference between let and function is that the initialization of a function binding occurs at the top of the enclosing block
Treating function like var has only been discussed in a couple of contexts.
At the global level function may need to bind like var rather than let for browser legacy compatibility reasons and in support of multiple top-level script blocks.
The other var related issue concerning function scoping also relates to legacy compatibility. ES5 and earlier does not allow for function declarations in blocks. However, all browser JS implementations do allow such declarations but they don't agree upon a common semantics. Some treat such function declarations like var declarations in terms of hoisting. FF does something that is somewhat similar to let, but not quite the same thing. In general, use of such function declarations are not interoperable among browsers, but there are some simple usage patterns that will produce the same results in all browsers.
At the last TC39 meeting it was hypothesized that the block nested function declaration patterns that are actually used inoperably on the web would continue to work if interpreted as lexical (let-like) declarations. On that basis, we decided that we would go forward with let-like scoping of block nested functions for all ES6 code. However, we will also experiment with early implementations to ensure that this does not "break the web". If it does, we will have to revisit the issue and probably only use let-like scoping for functions within strict mode.
For example:
function f(x) { bar(); // ??? for (let y of x) { function bar() { ... y ... } bar(); // OK } bar(); // ??? }
both var references outside of the for-let block result to their bindings in the scope that surrounds the declaration of f. If that is the global scope, they probably throw as unresolved references.
Here I think it's pretty natural to expect that the FunctionBody of
bar' has access to lexically scoped binding for
y'. But outside the block, the function bound to `bar' doesn't have any meaning.
outer scope reference
I only see two consistent answers here:
function foo(){} == let foo = function(){}
or
funciton foo(){} == var foo = function(){}
Hoisting the function definition doesn't make sense with block scope.
All declarations within a block are "hoisted" to the top of the block but can only be validly referenced after they have been initialized. The span between the top of the block and the point of initialization is called the "temporal dead zone". Function declarations are initialized at the top of the block. Let and const declaration are initialized when evaluated in statement list order.
On Feb 15, 2012, at 6:14 AM, Andy Wingo wrote:
On Wed, 2012-02-15 at 12:55 +0100, Andy Wingo wrote:
function f(x) { bar(); // ??? for (let y of x) { function bar() { ... y ... } bar(); // OK } bar(); // ??? }
Did I just get wtfjs'd here? Is this `bar' not a source element, and therefore this is a function expression and not a declaration?
No, in ES5 and earlier the above is not syntactically valid code, blocks contains StatementLists rather than SourceElements and FunctionDeclaration can not occur in StatementLists. Nor can a ExpressionStatement begin with an unparenthesized FunctionExpression. Even ignoring the let, there is no valid interpratation of the above in unextended ES5.
In ES6, FunctionDeclarations are allowed in StatementLists
Thanks for the note, Allen.
One remaining doubt:
On Wed, 2012-02-15 at 09:02 -0800, Allen Wirfs-Brock wrote:
At the last TC39 meeting it was hypothesized that the block nested function declaration patterns that are actually used inoperably on the web would continue to work if interpreted as lexical (let-like) declarations.
What about "conditional function definition"?
E.g.:
or Oliver's comments here:
bugs.webkit.org/show_bug.cgi?id=27226#c5
It seems like a valid use case that needs an answer. The answer might be, "just use declarations, assignments, and function expressions"; dunno.
,
Andy
On Feb 15, 2012, at 9:53 AM, Andy Wingo wrote:
Thanks for the note, Allen.
One remaining doubt:
On Wed, 2012-02-15 at 09:02 -0800, Allen Wirfs-Brock wrote:
At the last TC39 meeting it was hypothesized that the block nested function declaration patterns that are actually used inoperably on the web would continue to work if interpreted as lexical (let-like) declarations.
What about "conditional function definition"?
E.g.:
or Oliver's comments here:
bugs.webkit.org/show_bug.cgi?id=27226#c5
It seems like a valid use case that needs an answer. The answer might be, "just use declarations, assignments, and function expressions"; dunno.
This is precisely what we discussed at the last TC39 meeting.
if (predicate) { function foo() {alert("true")} } else { function foo() {alert("false")} } foo();
does not work interoperability among existing browsers. Under the proposed semantics, the above has a separate block scoped binding for foo in each of the if clauses and the outer reference would most likely be a reference error.
What does work interoperability is something like
if (predicate) { function foo() {alert("true")} foo(); } //note no else clause with an alternative foo definition and no reference to foo outside of the block.
If somebody wants to conditionally define a function an appropriate way to do so would be:
let foo; if (predicate) { foo=function foo() {alert("true")}; } else { foo=function foo() {alert("false")}; } foo();
or perhaps
function altFoo1() {alert("true")} function altFoo2() {alert("false")} let foo; if (predicate) { foo=altFoo1}; } else { foo=altFoo2; } foo();
Finally, remember that this is all contingent upon completing web breakage experiments.
Hello again ecmascriptians,
There has been some discussion about treating function declarations as "var" declarations. While I understand the concerns about compatibility, I think it will be surprising to programmers.
For example:
function f(x) { bar(); // ??? for (let y of x) { function bar() { ... y ... } bar(); // OK } bar(); // ??? }
Here I think it's pretty natural to expect that the FunctionBody of
bar' has access to lexically scoped binding for
y'. But outside the block, the function bound to `bar' doesn't have any meaning.I only see two consistent answers here:
function foo(){} == let foo = function(){}
or
funciton foo(){} == var foo = function(){}
Hoisting the function definition doesn't make sense with block scope.
Andy