arguments.callee in Harmony

# Charles Jolley (16 years ago)

First an introduction: I am the lead developer of SproutCore (www.sproutcore.com ), an open source framework for building client-side applications in
HTML5. SproutCore is used in several large projects including Apple's
MobileMe and iwork.com, OtherInbox, and some other large projects that
are not yet public.

Point is, we write very large applications in JavaScript and HTML5.
I've been following ES5/Harmony closely. By and large I am very
excited about the features to be added. One critical feature (imo)
that is missing, however, is "arguments.callee" - at least some way to
identify the current function that is executing.

I spoke with Brenden Eich and Yehuda Katz about this on Friday and
talked to Doug Crockford about it today and they suggested I email
this list to make my case so here it is:

USE CASE

Currently SproutCore implements a class-like system on top of
JavaScript. That is, you can have "classes" with "subclasses" and you
can instantiate both. SproutCore is not strictly classical, and JS is
not class-based of course, but I think this is a pattern that many
developers commonly want to implement and use in JS.

The problem comes with implementing methods that "override" methods in
superclasses. Take the following example [extend() is the SproutCore
function that creates a new "subclass"]:

ClassA = SC.Object.extend({

foo: function() { // do something }

});

ClassB = ClassA.extend({

// NOTE: overrides foo in classA! foo: function() { // call ClassA.foo(); // do something else }

});

--

Now the question is, how can ClassB.foo() call ClassA.foo() in a
generic fashion?

I could force developers to hard code this knowledge (i.e. when
implementing ClassB.foo() you have to explicitly call ClassA.foo.apply (this)) but this is prone to developer error and also makes the code
not easily transportable from one method to another; violating the
sort of "copy-and-paste" ethos that is part of JavaScript.

I've been told that I could name the functions. e.g.:

ClassB = ClassA.extend({ foo: function foo() { // ..code } });

Somehow that should solve my problem, though I can't really work out
how. But regardless, asking developers to name each method twice in
the declaration is also error prone and fragile.

--

The way I solve this currently is to implement extend() so that when
it copies an overloaded method, it sets a property ("base") on the
Function to point to the Function it is overloading. In the example
above, for example, this means that ClassB.foo.base === ClassA.foo.

This way I can write a generic call to "super" like so:

ClassB = ClassA.extend({

foo: function() { arguments.callee.base.apply(this, arguments); // calls super! // other code }

});

--

I realize this is not the most elegant looking code, but currently its
the only way I can figure out to implement the "super" pattern in a
generic way in JS.

SOLUTIONS?

With ES5, arguments.callee is gone.

One suggestion Brenden had was to perhaps include a "thisFunction" or
some such property that returns the current function instance you are
in. This seems useful to me for a bunch of other meta-programming
patterns as well but would certainly work here.

Another solution suggested by Douglas would be to provide a way for a
function to get its current "name" AND for ECMAScript to follow a
convention that anonymous functions when declared as part of an object
literal take on the name they are assigned to by default.

Either of these would work for this pattern; there are probably other
solutions as well.

I don't really care what ends up in the final implementation only that
there is some way to generically implement the ultra-common class- based-with-method-overloading pattern in ECMAScript >= 5 without

jumping through some crazy hoops.

--

So that's my best argument on this. Any suggestions of alternative
implementations that will work in ECMAScript 5? If this is a
limitation of the new language, what I can do to agitate for something
to be added?

Thanks,

# Oliver Hunt (16 years ago)

On Sep 24, 2009, at 2:56 PM, Charles Jolley wrote:

Now the question is, how can ClassB.foo() call ClassA.foo() in a
generic fashion?

I could force developers to hard code this knowledge (i.e. when
implementing ClassB.foo() you have to explicitly call
ClassA.foo.apply(this)) but this is prone to developer error and
also makes the code not easily transportable from one method to
another; violating the sort of "copy-and-paste" ethos that is part
of JavaScript.

I've been told that I could name the functions. e.g.:

ClassB = ClassA.extend({ foo: function foo() { // ..code } });

Somehow that should solve my problem, though I can't really work out
how. But regardless, asking developers to name each method twice in
the declaration is also error prone and fragile.

--

The way I solve this currently is to implement extend() so that when
it copies an overloaded method, it sets a property ("base") on the
Function to point to the Function it is overloading. In the example
above, for example, this means that ClassB.foo.base === ClassA.foo.

This way I can write a generic call to "super" like so:

ClassB = ClassA.extend({

foo: function() { arguments.callee.base.apply(this, arguments); // calls super! // other code }

});

Given your example, a named function expression would do the job
trivially:

ClassB = ClassA.extend({

foo: function foo() { foo.base.apply(this, arguments); // calls super! // other code }

});

It is likely to be both faster and safer than arguments.callee as both
arguments and callee can be overridden, and the lookup up for 'foo' is
guaranteed.

One other thing to consider is that arguments.callee is only invalid
in strict mode -- arguments.callee will continue to work fine in the
general case.

# Charles Jolley (16 years ago)

Given your example, a named function expression would do the job
trivially:

ClassB = ClassA.extend({

foo: function foo() { foo.base.apply(this, arguments); // calls super! // other code }

});

This works but, as I said, this approach has a couple of drawbacks:

  1. The function has to be named twice (foo: function foo()). This
    makes the code harder to read (especially with long names) and its not
    very developer-friendly since its pointless cruft.

  2. This is also fragile. If you forget the second name, the code
    would still parse but it will be seriously broken. Additionally, if
    you decided to rename the function you would also have to edit the
    code on the inside. Hurts the "copy and paste" aspect of it.

In general I think this particular approach is not developer friendly
enough.

--

It is likely to be both faster and safer than arguments.callee as
both arguments and callee can be overridden, and the lookup up for
'foo' is guaranteed.

Agreed on the faster side of things but, as I said, I think there are
developer-friendlyness issues with this approach that make it
unsuitable as a general solution for this pattern.

I would rather have a way to get at the currently running function w/o
having to go through arguments. I have no love lost with arguments. :-)

One other thing to consider is that arguments.callee is only invalid
in strict mode -- arguments.callee will continue to work fine in the
general case.

True. My assumption is that strict mode is defined so that you can
continue to run older code until you can transition it. in other
words, one should aspire to convert all of their code to strict mode
at some time; compatibility mode is intended just to help transition.

In that is the case, then "strict" mode should be able to implement a
common design pattern like method overloading in a friendly way,
otherwise developers may never convert.

# Oliver Hunt (16 years ago)

On Sep 24, 2009, at 3:55 PM, Charles Jolley wrote:

Given your example, a named function expression would do the job
trivially:

ClassB = ClassA.extend({

foo: function foo() { foo.base.apply(this, arguments); // calls super! // other code }

});

This works but, as I said, this approach has a couple of drawbacks:

  1. The function has to be named twice (foo: function foo()). This
    makes the code harder to read (especially with long names) and its
    not very developer-friendly since its pointless cruft.

  2. This is also fragile. If you forget the second name, the code
    would still parse but it will be seriously broken. Additionally, if
    you decided to rename the function you would also have to edit the
    code on the inside. Hurts the "copy and paste" aspect of it.

In general I think this particular approach is not developer
friendly enough.

The function expression name is fairly irrelevant, so you could have a
standard style guideline foo : function callee() { callee.base.apply ... }

# P T Withington (16 years ago)

On 2009-09-24, at 17:56, Charles Jolley wrote:

I've been told that I could name the functions. e.g.:

ClassB = ClassA.extend({ foo: function foo() { // ..code } });

Somehow that should solve my problem, though I can't really work out
how. But regardless, asking developers to name each method twice in
the declaration is also error prone and fragile.

OpenLaszlo have a similar scheme (bit.ly/1E6xig), but have a
pre-processor that lets the developer write in something closer to
'real' classes (based on ActionScript 3 and what was proposed for es4,
before we were so rudely interrupted). Because of the pre-processor
we could just re-write our uses of arguments.callee to use named
function expressions -- for platforms that implement the named
function expressions correctly.

My biggest concern therefore is not that arguments.callee is going
away, but that named function expressions have been mis-implemented so
many times before (e.g., early versions of webkit and JScript to this
day: bit.ly/19QxUO).

# Charles Jolley (16 years ago)

In general I think this particular approach is not developer
friendly enough. The function expression name is fairly irrelevant, so you could have
a standard style guideline foo : function callee() { callee.base.apply ... }

This is actually a really interesting idea. I'll try to use it. In
SproutCore's particular case we have a pre-processor that can insert
this also, just like PTW says for OpenLazlo. It's still annoying for
those developers without a pre-processor to help them but better.

--

I'm curious, why not just give anonymous functions a default name like
"callee". Or perhaps have "callee" defined in a function scope to
represent the function? That seems to be exactly the same as the
above; it just makes it easier for developers. Is there a perf issue
here?

# Brendan Eich (16 years ago)

On Sep 24, 2009, at 4:06 PM, Charles Jolley wrote:

I'm curious, why not just give anonymous functions a default name
like "callee". Or perhaps have "callee" defined in a function scope
to represent the function? That seems to be exactly the same as the
above; it just makes it easier for developers. Is there a perf
issue here?

No, there's simply a backward compatibility problem. Anonymous
functions do not inject any such name on the scope chain (in any
object, new or expected, on the scope chain). Changing the language to
inject callee (even in an ES5 declarative envirnment frame) is not
backward compatible and probably will break some content out there
that uses callee in an outer scope to mean something else.

# Yehuda Katz (16 years ago)

What I'd like to know is what the rationale for removing arguments.callee from strict mode is. Is it a performance problem? If so, have implementors tried other solutions at compile-time before agitating for the removal of a language feature? The argument that this is simply a strict mode change falls flat when we're also told that Harmony will be built on ES5 strict mode. As far as I'm concerned, anything missing in strict mode is effectively being removed from the language.

# Breton Slivka (16 years ago)

On Fri, Sep 25, 2009 at 9:26 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Sep 24, 2009, at 4:06 PM, Charles Jolley wrote:

I'm curious, why not just give anonymous functions a default name like "callee".  Or perhaps have "callee" defined in a function scope to represent the function?  That seems to be exactly the same as the above; it just makes it easier for developers.  Is there a perf issue here?

No, there's simply a backward compatibility problem. Anonymous functions do not inject any such name on the scope chain (in any object, new or expected, on the scope chain). Changing the language to inject callee (even in an ES5 declarative envirnment frame) is not backward compatible and probably will break some content out there that uses callee in an outer scope to mean something else.

/be


es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss

this discussion reminds me a little bit of the aspect oriented pattern that has been used in javascript on occasion. The idea there is that it's possible to replace a function with a wrapped version of the function that has the original function in the outer scope.

So for instance:

var myarray = [3,4,5]; myarray.toString = (function () { var ofunc = myarray.toString; return function () { return "Extended toString: "+ofunc.apply(this,arguments); } })()

myarray.toString(); //returns "Extended toString: 3,4,5"

This pattern can be generalised:

function extendFunc (object, methodname, func) { var ofunc = object[methodname]; object[methodname]=function () { return func.apply(object,[ofunc].concat(arguments)); }

}

you can see that in that version, the new function recieves the old function as its first parameter.

Now, my specific implementation has almost certainly got some flaws in it (not a lot of error checking or robustness), I'm sure- and people will argue about the details. It's the basic idea of aspect orientation that I'm trying to get at here.

The reason I'm bringing this up though is that this is a simple highly general building block that can be used to build the super abstraction, I believe (among many other useful abstractions). It's also implementable in current ecmascript, and it's something that could become canonized as a native function in a future version of ecmascript- similar to the array extras that were first in the prototype library, but are now in ecmascript 5.

There might be even more clever ways to build it so that it doesn't require an extra parameter (using "with" perhaps?) that could be used now, but built in native code and optimised in future editions. Anyway, I just thought I would put that out there for discussion.

# Breton Slivka (16 years ago)

x

On Fri, Sep 25, 2009 at 9:31 AM, Yehuda Katz <wycats at gmail.com> wrote:

What I'd like to know is what the rationale for removing arguments.callee from strict mode is. Is it a performance problem? If so, have implementors tried other solutions at compile-time before agitating for the removal of a language feature? The argument that this is simply a strict mode change falls flat when we're also told that Harmony will be built on ES5 strict mode. As far as I'm concerned, anything missing in strict mode is effectively being removed from the language. -- Yehuda

esdiscuss/2009-March/008970

# Yehuda Katz (16 years ago)

My general concern about strict mode is that it seems to be the result of a smoke-filled-room compromise. I'd like to see the perspective of JavaScript practitioners consulted about the strict mode definition (especially if it's going to form the basis for ES Harmony).

# Breton Slivka (16 years ago)

On Fri, Sep 25, 2009 at 10:49 AM, Breton Slivka <zen at zenpsycho.com> wrote:

x

On Fri, Sep 25, 2009 at 9:31 AM, Yehuda Katz <wycats at gmail.com> wrote:

What I'd like to know is what the rationale for removing arguments.callee from strict mode is. Is it a performance problem? If so, have implementors tried other solutions at compile-time before agitating for the removal of a language feature? The argument that this is simply a strict mode change falls flat when we're also told that Harmony will be built on ES5 strict mode. As far as I'm concerned, anything missing in strict mode is effectively being removed from the language. -- Yehuda

esdiscuss/2009-March/008970

Apologies with the curt message. I seem to have suddenly lost my ability to operate my computer competantly.

I meant to say, that the rational has been discussed at length in the past. Here's the link to the mailing list thread about it:

esdiscuss/2009-March/008970

# Yehuda Katz (16 years ago)

Thanks for the pointer. It seems like the argument against it is security: if I pass you my arguments object, you now have access to the original function, which violates the POLA. Isn't the fact (pointed out by Allen in the original thread) that arguments.callee can be deleted a mitigating factor here?

Aren't there other fixes that would work as well (such as making arguments.callee only available lexically) without completely removing the feature.

On a related topic, would you mind pointing me at the thread where the removal of with() was discussed?

# Luke Smith (16 years ago)

The use of named function expressions is not correctly implemented by
at least IE 6-8.

The basic use case works fine (e.g.) var foo = function callee() { // do something setTimeout(callee, 10); };

But declared as (function callee() { // do something setTimeout(callee, 10); })();

the name callee leaks to the enclosing scope.

Also, the callee function inside the function body is not the same
function. foo !== callee in IE.

So if you use the function as a namespace, callee.someProperty = "foo"
is not exposed on foo.someProperty.

See lucassmith.name/pub/named_func_exp.html

Luke

# Fabian Jakobs (16 years ago)

we have exactly the same problem in qooxdoo www.qooxdoo.org. We use

arguments.callee.base.call(this, ...);

in the same way. Using named functions isn't a solution for us because it would break IE compatibility. In this example

ClassB = ClassA.extend({

foo: function foo() { foo.base.apply(this, arguments); // calls super! // other code }

});

"foo" would leak into the global namespace due to implementation bugs in JScript yura.thinkweb2.com/named-function-expressions. Right now I

don't see a good solution for this in strict mode.

Best Fabian

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 4:14 AM, Fabian Jakobs wrote:

"foo" would leak into the global namespace due to implementation
bugs in JScript yura.thinkweb2.com/named-function-expressions.
Right now I don't see a good solution for this in strict mode.

But strict mode is not supported in current or downrev IE. So it is
imperative that Microsoft fix these old named function expression name- binding bugs before, or at the same time as, script mode along with
all of ES5 are supported in a future version of IE.

We talked about this issue yesterday at the end of the TC39 meeting. I
think Allen had to leave but I mailed him about it.

# Charles Jolley (16 years ago)

But strict mode is not supported in current or downrev IE. So it is
imperative that Microsoft fix these old named function expression
name-binding bugs before, or at the same time as, script mode along
with all of ES5 are supported in a future version of IE.

Wouldn't that violate the principle of strict mode? I thought the
idea was that writing code in Harmony could basically boil down to ES3
code if I wanted to. But in this case it wouldn't; the same code
written for Harmony would appear valid ES3 but would fail.

Sorry if I'm misunderstanding the goal here. I thought the point of
"use strict" is the same code could still run on older browsers who
don't understand the directive.

# Allen Wirfs-Brock (16 years ago)

I thought I had already responded to this, but I guess I missed the "send"...

The Microsoft intent is that when we support ES5 it will conform to the specified FunctionDeclaratiion/FunctionExpression semantics. However, this doesn't mean that ES5 semantics will be unilaterally imposed upon existing code that depends upon JScript 5.x semantics.

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 10:31 AM, Charles Jolley wrote:

But strict mode is not supported in current or downrev IE. So it is
imperative that Microsoft fix these old named function expression
name-binding bugs before, or at the same time as, script mode along
with all of ES5 are supported in a future version of IE.

Wouldn't that violate the principle of strict mode?

Which "that"? IE fixing bad old named function expression bugs
violates nothing, not ES5 or ES5 strict, and certainly not ES3 which
spec'ed how named function expressions bind their names (not in the
variable object! :-P).

Ok, forget IE bugs. It seems you are asking here whether "use strict";
in ES5 engines causes semantic changes that are not backward
compatible. The answer is that it does. See Annex C of ES5.

The cases are edgy, e.g., arguments[i] aliasing the i'th formal
parameter does not happen in an ES5 implementation under "use strict",
but it would when that code is fed to a pre-ES5 engine. And of course,
arguemnts.callee and the caller property poison pills, which throw in
ES5 strict but don't when code using them is fed into a pre-ES5 engine.

I thought the idea was that writing code in Harmony could basically
boil down to ES3 code if I wanted to. But in this case it wouldn't;
the same code written for Harmony would appear valid ES3 but would
fail.

Right. You have to test to see that "use strict"; code works the way
you expect in older browsers. If you steer clear of arguments objects
you don't have much to do. There's another runtime semantic change, to
eval (it can't create bindings in its dynamic scope), which under ES5
strict will break such examples (at least when I last checked) as
extjs.com, which did something like

function compile(...) { eval("var f = function() {" + compile_optimized_query(); + "}"); return f; }

This can be avoided by putting the var f = outside the eval, if you
parenthesize the lambda of course -- except Jack Slocum pointed out to
me that IE has bugs (perhaps only old IE versions still have these)
that bite this alternative! I'm not sure what the latest extjs code
does, since it seems hidden by dual licensing stuff I didn't click
through.

Sorry if I'm misunderstanding the goal here. I thought the point of
"use strict" is the same code could still run on older browsers who
don't understand the directive.

The code runs, but certain edge cases don't work the same. In old
engines they work as ever, in ES5 engines they may throw due to the
poison pills, or they may do something "better" (e.g., eval binds in
its own declarative environment).

A strict mode that only prevents programs from reaching runtime would
require too much static analysis.

A strict mode that adds runtime errors will require testing in ES5 and
pre-ES5 engines.

Given our inability to avoid runtime errors new with strict mode, we
managed to reach consensus on a few further runtime semantic changes.
If these cause trouble, we may find fewer strict mode uses than we'd
like to find. But it's hard to evaluate strict mode adoption for
success or failure based on whether these runtime changes bite back
-- we can't run the experiment a different way.

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 11:28 AM, Brendan Eich wrote:

function compile(...) { eval("var f = function() {" + compile_optimized_query(); + "}");

(Stray ; there in the + chain, oops.)

return f; }

This can be avoided by putting the var f = outside the eval, if you
parenthesize the lambda of course -- except Jack Slocum pointed out
to me that IE has bugs (perhaps only old IE versions still have
these) that bite this alternative! I'm not sure what the latest
extjs code does, since it seems hidden by dual licensing stuff I
didn't click through.

I did raise this issue in the course of strict mode's evolution, while
corresponding with Jack. I'm sorry I didn't follow up on it, although
the results may not have changed. Even if Microsoft fixes bad old
lambda bugs in JScript (glad to hear from Allen that it will in some
configuration or opt-in setting), old IE versions, and not-opted-in
scripts, will see the bugs and need the kind of structure Jack had to
use to compile optimized query functions.

Again, perhaps extjs has found another way. If not, it may be
impossible for the compiler function to be strict. But the ES5 strict
mode design may take that hit for some hoped-for greater good.

Given our inability to avoid runtime errors new with strict mode, we
managed to reach consensus on a few further runtime semantic
changes. If these cause trouble, we may find fewer strict mode uses
than we'd like to find. But it's hard to evaluate strict mode
adoption for success or failure based on whether these runtime
changes bite back -- we can't run the experiment a different way.

But we can hear feedback, esp. on this list, about hardship adopting
strict mode in early ES5 implementations. Mozilla's will be done
shortly, and it sounds like WebKit's is coming along quickly too.
Feedback based on two interoperable implementations in developers'
hands before the ES5 spec is stamped done would have been best. But
real-code/real-world feedback better late than never is welcome.

# Charles Jolley (16 years ago)

But we can hear feedback, esp. on this list, about hardship adopting
strict mode in early ES5 implementations. Mozilla's will be done
shortly, and it sounds like WebKit's is coming along quickly too.
Feedback based on two interoperable implementations in developers'
hands before the ES5 spec is stamped done would have been best. But
real-code/real-world feedback better late than never is welcome.

Once an implementation is out to play with you can be sure I will try
to build SproutCore with ES5 in strict mode and report back!

But just to be clear, after all of the discussion here, it appears
that the only way to implement a "call super" pattern in ES5 (without
resorting to closures as Breton suggested) would be to use the
following magic phrase everywhere I want to invoke super:

var IS_ES5_STRICT = /** detect ES5 strict mode somehow */

ClassB = ClassA.extend({ foo: function method() { (IS_ES5_STRICT ? method : arguments.callee).base.apply(this,
arguments); } });

Keep in mind that since I need to know the current function instance,
I can't abstract this code into a function. Everyone will just have
to know this magic phrase to "call super".

The SproutCore build tools already have a special symbol called
sc_super() that we can replace with the above code so I guess its not
a deal killer for me but still, thinking on behalf of casual JS coders
with no build tools, this seems not so great.

Or perhaps you would say that this kind of conditional code is what
you expect developers to write when they try to create JS that is
backward compatible with older JavaScript/JScript engines and ES5.

-Charles

PS. Is there an official way to detect that I am running code in ES5
strict mode? I can't find it in the spec.

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 2:24 PM, Charles Jolley wrote:

But we can hear feedback, esp. on this list, about hardship
adopting strict mode in early ES5 implementations. Mozilla's will
be done shortly, and it sounds like WebKit's is coming along
quickly too. Feedback based on two interoperable implementations in
developers' hands before the ES5 spec is stamped done would have
been best. But real-code/real-world feedback better late than never
is welcome.

Once an implementation is out to play with you can be sure I will
try to build SproutCore with ES5 in strict mode and report back!

But just to be clear, after all of the discussion here, it appears
that the only way to implement a "call super" pattern in ES5
(without resorting to closures as Breton suggested) would be to use
the following magic phrase everywhere I want to invoke super:

var IS_ES5_STRICT = /** detect ES5 strict mode somehow */

var IS_ES5_STRICT = typeof function(){return this;}() == "undefined";

or a variation (Mark Miller hat tip).

ClassB = ClassA.extend({ foo: function method() { (IS_ES5_STRICT ? method : arguments.callee).base.apply(this,
arguments); } });

But why wouldn't you use method always? Or callee, as Ollie suggested.
Agree it's annoying to have to add six letters of cruft after the
function keyword (with space separator) but once you do that, provided
you dodge IE bugs, why do you need arguments.callee at all?

PS. Is there an official way to detect that I am running code in ES5
strict mode? I can't find it in the spec.

if (! function () {return this;}()) { /* in strict mode */ }

is IIRC a shorter way, relying on falsy-ness of undefined, to test.

# Charles Jolley (16 years ago)

ClassB = ClassA.extend({ foo: function method() { (IS_ES5_STRICT ? method : arguments.callee).base.apply(this,
arguments); } });

But why wouldn't you use method always? Or callee, as Ollie
suggested. Agree it's annoying to have to add six letters of cruft
after the function keyword (with space separator) but once you do
that, provided you dodge IE bugs, why do you need arguments.callee
at all?

Precisely to avoid IE bugs. So maybe it will be written more like:

foo: function callee() { (SC.HAS_IEBUG ? arguments.callee : callee).base.apply(this,
arguments); }

where SC.HAS_IEBUG is true if IE version <= [last version of IE with
named-method bug]

PS. Is there an official way to detect that I am running code in
ES5 strict mode? I can't find it in the spec.

if (! function () {return this;}()) { /* in strict mode */ }

is IIRC a shorter way, relying on falsy-ness of undefined, to test.

Cool! Thanks.

Has anyone considered providing a more explicit way of testing for
this? Maybe a constant that is defined somewhere. Anyway, that's
what I'm going to do:

SC.HAS_STRICT = !(function() { return this;}());

:)

# David Flanagan (16 years ago)

Charles Jolley wrote:

Has anyone considered providing a more explicit way of testing for this? Maybe a constant that is defined somewhere.

Strict mode isn't a global on-or-off thing. Some functions can be strict while others aren't. So you can't capture it in a constant.

Anyway, that's what

I'm going to do:

SC.HAS_STRICT = !(function() { return this;}());

:)

If you're testing "does this browser support strict mode" then you'd better test it in an explicitly strict context:

SC.HAS_STRICT = (function() { "use strict"; return !(function() { return this; }()); }());

(Or something like that. I sure will be happy when there's an implementation to test this stuff against.)

Otherwise, what you're testing for is something like "is this code currently running in strict mode" or "was this library loaded under strict mode"?

David Flanagan
# Juriy Zaytsev (16 years ago)

On Sep 25, 2009, at 6:22 PM, David Flanagan wrote:

Charles Jolley wrote:

Has anyone considered providing a more explicit way of testing for
this? Maybe a constant that is defined somewhere.

Strict mode isn't a global on-or-off thing. Some functions can be
strict while others aren't. So you can't capture it in a constant.

Anyway, that's what

I'm going to do: SC.HAS_STRICT = !(function() { return this;}()); :)

If you're testing "does this browser support strict mode" then you'd
better test it in an explicitly strict context:

SC.HAS_STRICT = (function() { "use strict"; return !(function() { return this; }()); }());

Yep, that's what M. Miller showed earlier, IIRC. Only, I don't see a
reason to double wrap.

var IS_STRICT_MODE_SUPPORTED = (function(){ "use strict"; return ! this; })();

[...]

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 3:22 PM, David Flanagan wrote:

Charles Jolley wrote:

Has anyone considered providing a more explicit way of testing for
this? Maybe a constant that is defined somewhere.

Strict mode isn't a global on-or-off thing. Some functions can be
strict while others aren't. So you can't capture it in a constant.

Good point -- I was thinking of the useful fact to capture that the
browser supports strict mode (more below).

Anyway, that's what I'm going to do: SC.HAS_STRICT = !(function() { return this;}()); :)

If you're testing "does this browser support strict mode" then you'd
better test it in an explicitly strict context:

SC.HAS_STRICT = (function() { "use strict"; return !(function() { return this; }()); }());

(Or something like that. I sure will be happy when there's an
implementation to test this stuff against.)

You don't need two levels of functions. I just spaced out on the "use
strict"; in the anonymous function that returns |this|:

if (! function(){"use strict"; return this;}()) { /* strict mode
supported by the browser */ }

Otherwise, what you're testing for is something like "is this code
currently running in strict mode" or "was this library loaded under
strict mode"?

Thanks for wiping up after me. :-)

# David Flanagan (16 years ago)

Brendan Eich wrote:

SC.HAS_STRICT = (function() { "use strict"; return !(function() { return this; }()); }());

(Or something like that. I sure will be happy when there's an implementation to test this stuff against.)

You don't need two levels of functions. I just spaced out on the "use strict"; in the anonymous function that returns |this|:

Thanks to you (and also kangax) for pointing this out. I was assuming it was the strictness of the calling code that mattered, not of the callee. I now see that Section 10.4.3 specifies this clearly.

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 3:35 PM, Brendan Eich wrote:

Otherwise, what you're testing for is something like "is this code
currently running in strict mode" or "was this library loaded under
strict mode"?

Thanks for wiping up after me. :-)

David kindly pointed out that what I suggested originally is also
useful, and possibly it was what Charles wanted:

if (! function() { return this; }()) { /* this if statement is in strict code */ }

is useful in its own right. The other test is also useful:

if (! function() { "use strict"; return this; }()) { /* we're loaded in a browser supporting ES5 */ }

These are certainly idioms, especially the first test (as in "kick the
bucket", something an ES3 programmer could not understand based on the
terms in the code alone, only by also reading the ES5 spec or a book
based on it). Do they deserve sugar?

# Charles Jolley (16 years ago)

These are certainly idioms, especially the first test (as in "kick
the bucket", something an ES3 programmer could not understand based
on the terms in the code alone, only by also reading the ES5 spec or
a book based on it). Do they deserve sugar?

To me this would be something that should be sugar'd simply because it
will be so common. It would be nice not to end up with 100 different
ways of testing for ES5 strict mode support like we have today for
testing an Array.

Also, this would clearly convey to the casual developer that you
expect them to branch their code in some cases when trying to maintain
backwards compatibility.

I would think the most useful test would be "is strict mode available"
not "am I currently strict" since you can assume the later from the
former but not the reverse.

I suppose we could always just use browser sniffing. ;-)

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 4:07 PM, Charles Jolley wrote:

I would think the most useful test would be "is strict mode
available" not "am I currently strict" since you can assume the
later from the former but not the reverse.

Not sure how you can assume the latter from the former:

if (! function() { "use strict"; return this; }()) { /* I'm in an ES5-supporting browser, but strict is not necessarily
enabled here */ }

Again strict is a static property of code, so if there were a "use
strict" above the if, even if there were functions nesting around the
if, so long as the global code or one of the surrounding functions had
a "use strict" pragma, we'd be in strict mode in the commented "then"
clause.

But if not, then the "use strict" in the lambda in the if's condition
doesn't cause any change to what follows the condition.

I suppose we could always just use browser sniffing. ;-)

The idioms are better but you have a point :-P.

# Charles Jolley (16 years ago)

Not sure how you can assume the latter from the former:

if (! function() { "use strict"; return this; }()) { "use strict";

/* I'm an ES5 browser and DEFINITELY in strict mode now; doesn't
matter what came before me... */ }

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 4:22 PM, Charles Jolley wrote:

Not sure how you can assume the latter from the former:

if (! function() { "use strict"; return this; }()) { "use strict";

/* I'm an ES5 browser and DEFINITELY in strict mode now; doesn't
matter what came before me... */ }

Gotcha -- I wouldn't call that assuming, since you had to say "use
strict" twice. But I was being a logic nerd. Carry on!

# Brendan Eich (16 years ago)

On Sep 25, 2009, at 4:23 PM, Brendan Eich wrote:

On Sep 25, 2009, at 4:22 PM, Charles Jolley wrote:

Not sure how you can assume the latter from the former:

if (! function() { "use strict"; return this; }()) { "use strict";

/* I'm an ES5 browser and DEFINITELY in strict mode now; doesn't
matter what came before me... */ }

Gotcha -- I wouldn't call that assuming, since you had to say "use
strict" twice. But I was being a logic nerd. Carry on!

Today is short message with error-correcting followup day. :-/

You can't write "use strict"; as a pragma at the start of a block. You
have to do it at the start of a function body or program.

# Charles Jolley (16 years ago)

ok. then I retract my previous statement (that I could imply one from
the other). though I do think standard sugar for detecting strict
mode presence would be awesome!

# Jason Orendorff (16 years ago)

On Fri, Sep 25, 2009 at 4:24 PM, Charles Jolley <charles at sproutit.com> wrote:

But just to be clear, after all of the discussion here, it appears that the only way to implement a "call super" pattern in ES5 (without resorting to closures as Breton suggested) would be to use the following magic phrase everywhere I want to invoke super:

var IS_ES5_STRICT = /** detect ES5 strict mode somehow */

ClassB = ClassA.extend({  foo: function method() {    (IS_ES5_STRICT ? method : arguments.callee).base.apply(this, arguments);  } });

If I were a poor woodcutter who couldn't afford his own preprocessing tools, I would probably just write: ClassB.foo.base.apply(this, arguments)

I think web developers will be OK. Real Tools are getting more and more common.

# Mark S. Miller (16 years ago)

On Fri, Sep 25, 2009 at 2:24 PM, Charles Jolley <charles at sproutit.com> wrote:

PS. Is there an official way to detect that I am running code in ES5 strict mode? I can't find it in the spec.

To summarize:

A conventional pattern for testing "Am I in >= ES5 strict mode?" is

if (function(){return this;}()) {
  // I am not in strict mode
} else {
  // I am in strict mode, and therefore in >= ES5
}

A conventional pattern for testing "Am I in >= ES5?" is

if (function(){"use strict";return this;}()) {
  // I am in < ES5
} else {
  // I am in >= ES5
}

For these to work, it is important that browser makers rolling out ES5 features incrementally not cause the ES5-sides of these tests to succeed until their ES5 system is complete. In other words, much as it pains me to suggest this, please implement strict mode last.

# Yehuda Katz (16 years ago)

Or never? I can't shake the feeling that strict mode is a sop to people with minority opinions, but which will end up shaping the future of ES going forward.

# Garrett Smith (16 years ago)

On Fri, Sep 25, 2009 at 1:42 PM, Charles Jolley <charles at sproutit.com> wrote:

ClassB = ClassA.extend({ foo: function method() {  (IS_ES5_STRICT ? method : arguments.callee).base.apply(this, arguments); } });

But why wouldn't you use method always? Or callee, as Ollie suggested. Agree it's annoying to have to add six letters of cruft after the function keyword (with space separator) but once you do that, provided you dodge IE bugs, why do you need arguments.callee at all?

Precisely to avoid IE bugs.  So maybe it will be written more like:

foo: function callee() {  (SC.HAS_IEBUG ? arguments.callee : callee).base.apply(this, arguments); }

where SC.HAS_IEBUG is true if IE version <= [last version of IE with named-method bug]

That does not avoid jscript bug with named function expressions; it triggers it.

That is not "the only way" to implement inheritance. It apparently relies on a base property of the function object. That is not part of ECMAScript. What is it?

I suggest avoiding super calls. If you really must, then consider a standard approach that will work consistently across browsers, and in ES3 and ES5 strict:

ClassB.prototype = { foo: function() {   ClassA.prototype.foo.call(this); } };

[snip]

Garrett