substitutes for arguments and arguments.callee in ECMAScript 5

# Felipe Gasper (13 years ago)

I read with dismay about the deprecation of arguments.callee in ECMAScript 5.

I can appreciate the security concerns of passing the arguments object 

as a parameter to another method, but why not make that the “red flag” action rather than nixing “arguments” entirely?

e.g. this would cause a problem:

function() { other_func.call(this,arguments); }

..but this would not:


function() { var thisfunc = arguments.callee; }

Myself, I almost always do:

function() { other_func.apply(this,arguments); }

...which would not present the security concerns since the arguments object itself isn’t passed.

Where I find myself most frequently using the arguments object is to 

refer to the function itself, e.g. in YUI:


a_dialog.hideEvent.subscribe( function() { this.hideEvent.unsubscribe( arguments.callee ); console.log("This will only happen once."); } );

Am I to understand now that ECMASCript 5 will now have me type in:


var func; a_dialog.hideEvent.subscribe( func = function() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

...or, worse:


var func = function() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); }; a_dialog.hideEvent.subscribe( func );

The example I first gave is simple, straightforward, and (assuming one knows what arguments.callee is!) easy to grasp. The second is a little quirky because of the assignment within a function call parameter. The third is just unclear as to what the code is meant to do.

Granted, (as I have often felt like suggesting) maybe YUI should have a “subscribeOnce” method for its CustomEvent class for what I want to do. That aside, though, I think there are many uses for creating anonymous functions and referring to the function itself within the execution.

Is there really no way to accomplish this moving forward?? Few will miss “with”, and I welcome being made to set window properties explicitly, but nixing “callee” seems just unnecessary.

# Allen Wirfs-Brock (13 years ago)

The ultimate replacement for the arguments object is expected to be rest parameters and the spread operator: harmony:rest_parameters and harmony:spread

On Jan 5, 2011, at 11:30 AM, Felipe Gasper wrote:

Am I to understand now that ECMASCript 5 will now have me type in:


var func; a_dialog.hideEvent.subscribe( func = function() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

No, what you should type is:

a_dialog.hideEvent.subscribe( function func() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

# Felipe Gasper (13 years ago)

On 1/5/11 1:43 PM, Allen Wirfs-Brock wrote:

On Jan 5, 2011, at 11:30 AM, Felipe Gasper wrote:

Am I to understand now that ECMASCript 5 will now have me type in:


var func; a_dialog.hideEvent.subscribe( func = function() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

No, what you should type is:

a_dialog.hideEvent.subscribe( function func() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

Ah - thanks.

That would seem to work in most cases, but the subtle differences between

var foo = function() { ... }

and

function foo() { ... }

...make me a little uncertain. Crockford says in “The Good Parts” that “it turns out that most browsers allow function statements in if statements” (p113, emphasis added), but he doesn’t elaborate on which browsers that does/doesn’t mean. And there are differences of scope between the two declarations of the function that seem to invite subtle bugs.

# Brendan Eich (13 years ago)

On Jan 5, 2011, at 12:06 PM, Felipe Gasper wrote:

On 1/5/11 1:43 PM, Allen Wirfs-Brock wrote:

On Jan 5, 2011, at 11:30 AM, Felipe Gasper wrote:

Am I to understand now that ECMASCript 5 will now have me type in:


var func; a_dialog.hideEvent.subscribe( func = function() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

No, what you should type is:

a_dialog.hideEvent.subscribe( function func() { this.hideEvent.unsubscribe( func ); console.log("This will only happen once."); } );

Ah - thanks.

That would seem to work in most cases, but the subtle differences between

var foo = function() { ... }

and

function foo() { ... }

...make me a little uncertain. Crockford says in “The Good Parts” that “it turns out that most browsers allow function statements in if statements” (p113, emphasis added), but he doesn’t elaborate on which browsers that does/doesn’t mean. And there are differences of scope between the two declarations of the function that seem to invite subtle bugs.

Allen's revision of your example is not using any extension to ES3. It's using a named function expression.

Functions in blocks are an extension, implemented variously.

Unfortunately, even named function expressions have a notorious bug up through IE8 (IIRC) where the name is bound in the variable object.

# Allen Wirfs-Brock (13 years ago)

Just a couple additional points to make sure that Brendan and I playing tag-team aren't sowing further confusion about this topic:

...subscribe(function func() {...})

according to the ES standard is a different construct from either

var foo = function() { ... }

or

function foo() { ... }

In particularly, the latter two introduce a declaration for the name "func" into the surrounding scope so it can be referenced anywhere in that scope. The first function expression form introduces a declaration for "func" that is supposed to only be visible from within the {...} function body. Also, the function expression form has a well-defined meaning anywhere including in the compound statement blocks such as if-statements. The meaning of the latter two declaration forms are not defined by the standard when they occur within compound statement blocks. What they do, depends upon the browser.

IE, prior to IE9, didn't conform to the standard for function expressions and makes the name "func" visible in the surrounding scope. However, if all you want to do if be able to refer to "func" within the {...} body and not interfere with any declarations in the surrounding scope, even for IE <= 8, you can usually accomplish this by choosing a unique name instead of "func" that is highly unlike to be used in the surrounding scope. For example,

...subscribe(function myLikelyUniqueName() {...myLikelyUniqueName...})

# Juriy Zaytsev (13 years ago)

On Wed, Jan 5, 2011 at 3:48 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Just a couple additional points to make sure that Brendan and I playing tag-team aren't sowing further confusion about this topic:

...subscribe(function func() {...})

according to the ES standard is a different construct from either

var foo = function() { ... }

or

function foo() { ... }

In particularly, the latter two introduce a declaration for the name "func" into the surrounding scope so it can be referenced anywhere in that scope. The first function expression form introduces a declaration for "func" that is supposed to only be visible from within the {...} function body. Also, the function expression form has a well-defined meaning anywhere including in the compound statement blocks such as if-statements. The meaning of the latter two declaration forms are not defined by the standard when they occur within compound statement blocks. What they do, depends upon the browser.

IE, prior to IE9, didn't conform to the standard for function expressions and makes the name "func" visible in the surrounding scope.

There's a little more to it.

IE<9 actually creates 2 function objects when parsing NFE; not just leaks identifier to the enclosing scope. It might seem irrelevant at first, but if you consider that something like var f = function g(){ ... } will result in creation of 2 distinct function objects bound to f and g, it's easy to imagine how this could lead to all kinds of lovely bugs (e.g. when assigning to a property of function object; and having that property on f but not g, or vice versa).

[...]

# Mark S. Miller (13 years ago)

On Wed, Jan 5, 2011 at 12:48 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Just a couple additional points to make sure that Brendan and I playing tag-team aren't sowing further confusion about this topic:

...subscribe(function func() {...})

according to the ES standard is a different construct from either

var foo = function() { ... }

or

function foo() { ... }

In particularly, the latter two introduce a declaration for the name "func" into the surrounding scope so it can be referenced anywhere in that scope. The first function expression form introduces a declaration for "func" that is supposed to only be visible from within the {...} function body. Also, the function expression form has a well-defined meaning anywhere including in the compound statement blocks such as if-statements. The meaning of the latter two declaration forms are not defined by the standard when they occur within compound statement blocks. What they do, depends upon the browser.

The "latter two"? The function declaration form is indeed ambiguous as currently implemented across browsers. (And this ambiguity does not violate the spec, as such nested function declarations are outside the std grammar and so allowed under the Ch16 exemptions.) However, the "var" declaration form has no such ambiguity. The spec mandates var hoisting for the visibility of the "foo" variable, and that the initialization be equivalent to an assignment to "foo" occurring according to the textual placement of the initialization. In other words,

 function bar() {
     ...
     if (t) {
         ...
         var foo = function() { ... };
         ...
     }
     ...
 }

is equivalent to

 function bar() {
     var foo;
     ...
     if (t) {
         ...
         foo = function() { ... };
         ...
     }
     ...
 }

AFAIK, all browsers conform to the spec in this regard.

# Allen Wirfs-Brock (13 years ago)

Yes, there is more too it, as Kangax and MarkM point out. But I think we are at the point where we are just adding noise to this conversation. Felipe was just asking for an alternative to arguments.callee for referring to the value of a function expression from within the body of the function. A named function expression with a well chosen name is a very interoperable way to easily do this. All the other semantic subtleties and implementation variations in the handling of block nested declarations are not really all that relevant to straight-forward usage of that pattern.

# Garrett Smith (13 years ago)

On 1/5/11, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: [...]

the function expression form has a well-defined meaning anywhere including in the compound statement blocks such as if-statements. The meaning of the latter two declaration forms are not defined by the standard when they occur within compound statement blocks. What they do, depends upon the browser.

[...]

ES5 note uses some terminology that I don't understand.

"disallow usage"? "issue a warning"? What do those mean? It'd make sense to say "throw a SyntaxError". What does it mean to issue a warning? When does it happen? After the misplaced FD early (as early errors)? Or do warnings cause for abrupt completion?

I'd prefer "An implementation that handles FD as a Statement must issue a warning" but I think "issue a warning" should be defined.

# Brendan Eich (13 years ago)

On Jan 6, 2011, at 12:17 AM, Garrett Smith wrote:

On 1/5/11, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: [...]

the function expression form has a well-defined meaning anywhere including in the compound statement blocks such as if-statements. The meaning of the latter two declaration forms are not defined by the standard when they occur within compound statement blocks. What they do, depends upon the browser.

[...]

ES5 note uses some terminology that I don't understand.

I missed the "issue a warning" there at the bottom of 12, but there's also one in 14.1.

"disallow usage"? "issue a warning"? What do those mean? It'd make sense to say "throw a SyntaxError". What does it mean to issue a warning? When does it happen? After the misplaced FD early (as early errors)? Or do warnings cause for abrupt completion?

I'd prefer "An implementation that handles FD as a Statement must issue a warning" but I think "issue a warning" should be defined.

We are going to ban function declarations in blocks in strict mode, in Firefox 4. We have warning facilitiies too but they can generate a ton of noise without extra latch logic to suppress all but the first and avoid a console-spam storm. We're not going to warn about functions in blocks.

A recent function-in-block case we found involved an expectation of hoisting, either to block top (as proposed for Harmony, where the function binding would be block-local) or top of enclosing function or program (as IE and followers do). We evangelized the site successfully to reorder things. FYI, and a hopeful sign.

# Allen Wirfs-Brock (13 years ago)

Well, NOTES are non-normative and sometimes it is useful to be intentionally vague in such situations. There is no normative use of "warning" in the ES5 specification so there is no point in formally defining it. I think the intent of "issue a warning" should be clear enough to any language implementor. Since, such warnings are not normative requirements, its up to the implementor to decide how they interpret this informative recommendation.