Why does javascript concatenate function names in multiple assignment to properties? - javascript

This is an edge case and probably bad practice, but it made me curious about some js internals. Can anyone explain why chrome dev tools tells me that I have created a function named a.a.b.b here?
Note that this does not happen unless you are assigning to a property. Otherwise both a and b appear to refer to a function object named 'b':
By the way, I originally encountered this here when trying to answer my own question about dat.gui.js .

This has nothing to do with the language spec.
It's a DevTools enhancement for debugging convenience, which is ported recently in Chrome.
Remember what we used to do?
function F() {}
// notice it's a NAMED function expression
F.prototype.asdf = function _asdf() { debugger; };
var f = new F();
f.asdf();
Then in breakpoint debugging, we can find the method by its name _asdf from function call stack. Otherwise it's the pain in the ass to do that from a list of (anonymous function).
In latest Chrome, when you assign an anonymous function as an object property, an alias will be attached to it.
var a = {}, b = {};
a.a = b.b = function() { debugger; };
a.b = b.a = function _abba() { debugger; };
Remember, it's just a DevTools enhancement, the method remains anonymous:
a.a.name; // ""
a.b.name; // "_abba"
But it's very helpful in breakpoint debugging:
a.a();
a.b();
EDIT:
I'm not very sure why the alias is generated as a.a.b.b, it looks very easy but kind of... stupid. However, in practice we seldom do a.a = b.b = func... thing (lucky). Instead, we define a method in one place, and do inheritence when necessary, rather than copy reference directly.
So in a good programming practice, the alias should and would exactly reflect where you define the method. For example, alias Dog.bark in breakpoint clearly maps to Dog.prototype.bark in source code, even if it's called on a Puppy instance, and we don't have to do old school named function expression.
function Dog() {}
Dog.prototype.bark = function() { alert("Woof!") }; // anonymous function expression here
function Puppy() {}
Puppy.prototype = new Dog();
(new Puppy()).bark(); // break point alias -> Dog.bark
One more thing, when I discovered this feature, I can't stop thinking of it - does it imply that Chrome will implement ES6 class very soon? How exciting!

Related

Override JS method i Java style [duplicate]

I would like to override a Javascript built-in function with a new version that calls the original (similarly to overriding a method on a class with a version that calls super in many languages). How can I do this?
For example...
window.alert = function(str) {
//do something additional
if(console) console.log(str);
//super.alert(str) // How do I do this bit?
}
Store a reference to the original function in a variable:
(function() {
var _alert = window.alert; // <-- Reference
window.alert = function(str) {
// do something additional
if(console) console.log(str);
//return _alert.apply(this, arguments); // <-- The universal method
_alert(str); // Suits for this case
};
})();
The universal way is <original_func_reference>.apply(this, arguments) - To preserve context and pass all arguments. Usually, the return value of the original method should also be returned.
However, it's known that alert is a void function, takes only one argument, and does not use the this object. So, _alert(str) is sufficient in this case.
Note: IE <= 8 throws an error if you try to overwrite alert, so make sure that you're using window.alert = ... instead of alert = ....
There is no "super". Anyway, create a closure to "keep" around the original function-object.
Note the "self invoking function" that returns a new function-object (that is assigned to the window.alert property). The new function-object returned creates a closure around the variable original which evaluates to the original value of window.alert that was passed in to the "self invoking function".
window.alert = (function (original) {
return function (str) {
//do something additional
if(console) {
console.log(str)
}
original(str)
}
})(window.alert)
However, I believe some browsers may prevent alert and other built-ins from being modified...
Happy coding.
I'm assuming your question is how do you overwrite a built-in and still be able to call it. First off as a disclaimer, you should never overwrite built ins unless you have a good reason for doing it since it will make it impossible to debug/test.
This is how you would do it:
window._alert = window.alert;
window.alert = function(str) {
if(console) console.log(str);
window._alert(str);
}
How to do simple classical inheritance in Javascript:
SuperClass.call(this) // inherit from SuperClass (multiple inheritance yes)
How to override functions:
this.myFunction = this.myFunction.override(
function(){
this.superFunction(); // call the overridden function
}
);
The override function is created like this:
Function.prototype.override = function(func)
{
var superFunction = this;
return function()
{
this.superFunction = superFunction;
return func.apply(this,arguments);
};
};
Works with multiple arguments.
Fails when trying to override undefined or nonfunctions.
Makes "superFunction" a "reserved" word :-)
JavaScript does not use a classical inheritance model. There is a nice article here which describes a way to write your classes so that a similar syntax can be used, but it's not natively supported.
By using proxy object you can do this.
window.alert = new Proxy(window.alert , {
apply: function(target,that,args){
console && console.log(args.join('\n'));
target.apply(that,args)
}})

is there a way to inject privileged methods after an object is constructed?

I'm trying to write fully automated unit tests in JavaScript and I'm looking for a way to read some private variables in various JS functions. I thought I recalled a way to inject privileged members into a function/object (and found an overwhelming occurrence of "no such thing as private in JS") and yet I can't find any resources indicating how.
I'm trying to read through the properties of .prototype but if there's a way, someone out here would know where to direct me faster than I'd find on my own.
Thanks
Update
Privileged means a function that is usable from outside an object and has access to read "private" variables (variables otherwise unable to read from outside). See http://javascript.crockford.com/private.html for Crockford's explanations.
A sample of the function I'm trying to inject to is o2, where I need to validate the value of x (this is a simplified example, the actual code does some transformations and sends them off to other functions which I plan on testing separately).
var o2 = function() {
var x = 'me';
};
Update 2:
Thanks to everyone who's taken the time to respond. I'm seeing that the overwhelming response here is, "private is private" despite the commentary I see in other SA questions where people say "nothing in JS is private". I guess that is more like rhetorical commentary rather than what I was hoping was some kind of insight into a potential loophole I didn't yet know about.
Correct answer to your question
Even if we try to help you as much as we can, the correct answer to your question is simple:
...you can't
What good would closure variables be if we could access them directly from outside of closure?
if you had an object with private variables, they wouldn't be accessible outside function closure, which is the constructor function.
var c = function() {
var priv = "private";
this.get = function() {
return priv;
};
}
var o = new c();
o.injected = function() {
return priv; // ERROR!!!!!! priv doesn't exist in function scope
};
It is of course different matter if you enclose the whole thing inside a function closure but then you wouldn't have object privates, but rather function closure internals that would be shared among all prototypes within this closure.
(function(){
var intrn = "internal";
// omit "var" to make it global
c = function() {
this.get = function() {
return intrn;
};
};
// omit "var" to make it global
o = new c();
o.injected = function() {
return intrn; // WORKS
};
})();
o.injected();
but this is different than object privates...
Know that I am quite late to the party, but yes it is possible. Although AFAIK it requires use of eval() and a helper method:
function foo(){
var a = 'a';
this.addMethod = function(prop, val){
this[prop] = eval('(' + val.toString() + ')');
}
}
var bar = new foo();
bar.b = 'b';
var Fn = function(){return a + this.b}
bar.addMethod('getValue', Fn);
bar.getValue(); //yields 'ab'
You can use eval() in this case to redefine the function within the context of the closure. However, using eval() is dangerous, ESPECIALLY don't eval() any user input, EVER. However, for use in a testing suite/script it should be fine.
I'd suggest you read Crockford's treatise on how to achieve privileged and private in javascript objects.
If by privileged, you mean methods that are not accessible outside the object, but are callable by other methods and are actually methods on the object, then there really isn't any way to do that because one you put a method on the object, javascript let's anyone call it.
If you just wanted to add a function that was only callable by methods, you could do that by having a private member variable (see how Crockford implements that) that was an object and you could put the methods on that object. Then all existing methods (implemented the way Crockford suggests in the constructor to have access to the private member data) could call those methods. They would technically be members of a different object, but could be given private access to your object's instance data if needed.
It's all pretty convoluted so you'd probably get a better answer if you described the problem you're really trying to solve.
I don't think you can do that. Consider the following code:
function foo() {
var privateVar = 10;
this.privilegedMethod = function() {
return privateVar;
}
}
var obj = new foo();
Here, privilegedMethod has access to privateVar, since it's part of the closure created when the method was defined. Of course, you can always attach a new method to the object after instantiation:
obj.newMethod = function() { return "hello" };
alert(obj.newMethod()); // alerts "hello"
But this new method won't have access to privateVar.

anonymous and named functions doubt

While debugging I found that this kind of functions:
var f = function() {};
Appear on the stack trace of firebug or webkits dev console as anonymous, and rightfully so.
Also I've seen people defining these as:
var someName = function otherName(){};
Which are quite weird. Note that here you cant call otherName() from anywhere but the body of otherName itself. From everywhere else you have to use someName().
My questions are:
Is there any problem in naming a function different from the var where it's stored?
Does var a = function a(){} makes any difference besides just showing the name in the stack trace ?
Any other tip/suggestion on this topic :)
There's no problem with assigning a function named f to a variable named a.
A nice reference on functions is https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope. Of particular interest is the section entitled "Function constructor vs. function declaration vs. function expression" which has a detailed discussion the on distinction between the function name and the variable the function is assigned to. (You may have seen this already.)
My guess is the reason that the debugger prints something like
var a = function a() {}
is that the function's name appears when the function value itself is serialized. The debugger is giving you all the information it has.
Note that here you cant call otherName() from anywhere but the body of otherName itself.
Not in IE (including IE8).
See http://kangax.github.com/nfe/#jscript-bugs for more named function bugs, very nice article.
Not really. With var a = function b() {} the named function isn't hoisted and its prototype is not meaningfully modifiable. Take the following code for example:
function foo() {
}
foo.prototype.bar = "hi";
var a = new foo(); // this inherits from function foo() above
var b = function foo() {}; // this has nothing to do with the above
console.log(a.bar); // returns "hi" due to inheritance
console.log(b.bar); // returns undefined; its prototype is a new named
// function
var c = function() {};
var d = function d() {};
console.log(c.name); // returns ""
console.log(d.name); // returns "d"
AFAICT, the main useful method is having the name easily accessible (mostly for the var a = function a(){} form), which might be helpful in some edge cases, I'd think mostly in error handling.

How to get the function name from within that function?

How can I access a function name from inside that function?
// parasitic inheritance
var ns.parent.child = function() {
var parent = new ns.parent();
parent.newFunc = function() {
}
return parent;
}
var ns.parent = function() {
// at this point, i want to know who the child is that called the parent
// ie
}
var obj = new ns.parent.child();
In ES6, you can just use myFunction.name.
Note: Beware that some JS minifiers might throw away function names, to compress better; you may need to tweak their settings to avoid that.
In ES5, the best thing to do is:
function functionName(fun) {
var ret = fun.toString();
ret = ret.substr('function '.length);
ret = ret.substr(0, ret.indexOf('('));
return ret;
}
Using Function.caller is non-standard. Function.caller and arguments.callee are both forbidden in strict mode.
Edit: nus's regex based answer below achieves the same thing, but has better performance!
ES6 (inspired by sendy halim's answer below):
myFunction.name
Explanation on MDN. As of 2015 works in nodejs and all major browsers except IE.
Note: On bound functions this will give "bound <originalName>". You will have to strip the "bound " if you want to get the original name.
ES5 (inspired by Vlad's answer):
If you have a reference to the function, you can do:
function functionName( func )
{
// Match:
// - ^ the beginning of the string
// - function the word 'function'
// - \s+ at least some white space
// - ([\w\$]+) capture one or more valid JavaScript identifier characters
// - \s* optionally followed by white space (in theory there won't be any here,
// so if performance is an issue this can be omitted[1]
// - \( followed by an opening brace
//
var result = /^function\s+([\w\$]+)\s*\(/.exec( func.toString() )
return result ? result[ 1 ] : '' // for an anonymous function there won't be a match
}
I have not run unit tests on this, or verified implementation
differences, but in principle it should work, if not leave a comment.
Note: won't work on bound functions
Note: that caller and callee are considered deprecated.
[1] I include it here because it is legal and often enough syntax highlighting tools fail to take into account the white space between function name and parenthesis. On the other hand, I'm not aware of any implementation of .toString() that will include white space here, so that's why you can omit it.
As an answer to the original question, I would drop parasitic inheritance and go for some more traditional OOP design patterns. I wrote a TidBits.OoJs to comfortably write OOP code in JavaScript with a feature set mimicking C++ (not yet complete, but mostly).
I see from the comments that you would like to avoid passing information parent needs to it's constructor. I must admit that traditional design patterns won't save you from that one though, since it is generally a considered a good thing to make your dependencies obvious and enforced.
I would also suggest to steer away from anonymous functions. They only make debugging and profiling a PITA because everything just shows up as "anonymous function", and there is no benefit to them that I'm aware of.
what you're doing is assigning unnamed function to a variable. you probably need named function expression instead ( http://kangax.github.com/nfe/ ).
var x = function x() {
console.log( arguments.callee.name );
}
x();
however I'm not sure how much cross-browser that is; there's an issue with IE6 that makes you function's name leak to the outer scope. also, arguments.callee is kind of deprecated and will result in error if you're using strict mode.
It looks like the most stupid thing, that I wrote in my life, but it's funny :D
function getName(d){
const error = new Error();
const firefoxMatch = (error.stack.split('\n')[0 + d].match(/^.*(?=#)/) || [])[0];
const chromeMatch = ((((error.stack.split('at ') || [])[1 + d] || '').match(/(^|\.| <| )(.*[^(<])( \()/) || [])[2] || '').split('.').pop();
const safariMatch = error.stack.split('\n')[0 + d];
// firefoxMatch ? console.log('firefoxMatch', firefoxMatch) : void 0;
// chromeMatch ? console.log('chromeMatch', chromeMatch) : void 0;
// safariMatch ? console.log('safariMatch', safariMatch) : void 0;
return firefoxMatch || chromeMatch || safariMatch;
}
d - depth of stack. 0 - return this function name, 1 - parent, etc.;
[0 + d] - just for understanding - what happens;
firefoxMatch - works for safari, but I had really a little time for testing, because mac's owner had returned after smoking, and drove me away :'(
Testing:
function limbo(){
for(let i = 0; i < 4; i++){
console.log(getName(i));
}
}
function lust(){
limbo();
}
function gluttony(){
lust();
}
gluttony();
Result:
Chrome:
Fitefox:
This solution was creating only just for fun! Don't use it for real projects. It does not depend on ES specification, it depends only on browser realization. After the next chrome/firefox/safari update it may be broken.
More than that there is no error (ha) processing - if d will be more than stack length - you will get an error;
For other browsers error's message pattern - you will get an error;
It must work for ES6 classes (.split('.').pop()), but you sill can get an error;
Any constructor exposes a property name, which is the function name. You access the constructor via an instance (using new) or a prototype:
function Person() {
console.log(this.constructor.name); //Person
}
var p = new Person();
console.log(p.constructor.name); //Person
console.log(Person.prototype.constructor.name); //Person
This might work for you:
function foo() { bar(); }
function bar() { console.log(bar.caller.name); }
running foo() will output "foo" or undefined if you call from an anonymous function.
It works with constructors too, in which case it would output the name of the calling constructor (eg "Foo").
More info here: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller
They claim it's non-standard, but also that it's supported by all major browsers: Firefox, Safari, Chrome, Opera and IE.
You can't. Functions don't have names according to the standard (though mozilla has such an attribute) - they can only be assigned to variables with names.
Also your comment:
// access fully qualified name (ie "my.namespace.myFunc")
is inside the function my.namespace.myFunc.getFn
What you can do is return the constructor of an object created by new
So you could say
var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc
You could use this, for browsers that support Error.stack (not nearly all, probably)
function WriteSomeShitOut(){
var a = new Error().stack.match(/at (.*?) /);
console.log(a[1]);
}
WriteSomeShitOut();
of course this is for the current function, but you get the idea.
happy drooling while you code
You could use Function.name:
In most implementations of JavaScript, once you have your constructor's reference in scope, you can get its string name from its name property (e.g. Function.name, or Object.constructor.name
You could use Function.callee:
The native arguments.caller method has been deprecated, but most browsers support Function.caller, which will return the actual invoking object (its body of code):
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fcaller
You could create a source map:
If what you need is the literal function signature (the "name" of it) and not the object itself, you might have to resort to something a little more customized, like creating an array reference of the API string values you'll need to access frequently. You can map them together using Object.keys() and your array of strings
You can use name property to get the function name, unless you're using an anonymous function
For example:
var Person = function Person () {
this.someMethod = function () {};
};
Person.prototype.getSomeMethodName = function () {
return this.someMethod.name;
};
var p = new Person();
// will return "", because someMethod is assigned with anonymous function
console.log(p.getSomeMethodName());
now let's try with named function
var Person = function Person () {
this.someMethod = function someMethod() {};
};
now you can use
// will return "someMethod"
p.getSomeMethodName()
You can use constructor name like:
{your_function}.prototype.constructor.name
this code simply return name of a method.
as part as ECMAScript 6 you can use Function.name method
function doSomething() {}
alert(doSomething.name); // alerts "doSomething"
I know this is a old question but lately I've been facing some similar issue while trying to decorate some React Component's methods, for debugging purposes. As people already said, arguments.caller and arguments.callee are forbidden in strict mode which is probably enabled by default in your React transpiling. You can either disable it, or I've been able to come up with another hack, because in React all class functions are named, you can actually do this:
Component.prototype.componentWillMount = function componentWillMount() {
console.log('Callee name: ', this.__proto__.constructor.toString().substr(0,30));
...
}
This worked for me.
function AbstractDomainClass() {
this.className = function() {
if (!this.$className) {
var className = this.constructor.toString();
className = className.substr('function '.length);
className = className.substr(0, className.indexOf('('));
this.$className = className;
}
return this.$className;
}
}
Test code:
var obj = new AbstractDomainClass();
expect(obj.className()).toBe('AbstractDomainClass');
I had a similar problem and I solved it as follows:
Function.prototype.myname = function() {
return this.toString()
.substr( 0, this.toString().indexOf( "(" ) )
.replace( "function ", "" );
}
This code implements, in a more comfortable fashion, one response I already read here at the top of this discussion.
Now I have a member function retrieving the name of any function object.
Here's the full script ...
<script language="javascript" TYPE="text/javascript">
Function.prototype.myname = function() {
return this.toString()
.substr( 0, this.toString().indexOf( "(" ) )
.replace("function ", "" );
}
function call_this( _fn ) { document.write( _fn.myname() ); }
function _yeaaahhh() { /* do something */ }
call_this( _yeaaahhh );
</script>
If I understood what you wanted to do, this is what I do inside a function constructor.
if (!(this instanceof arguments.callee)) {
throw "ReferenceError: " + arguments.callee.name + " is not defined";
}
This will work in ES5, ES6, all browsers and strict mode functions.
Here's how it looks with a named function.
(function myName() {
console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at myName (<anonymous>:2:15)
Here's how it looks with an anonymous function.
(() => {
console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15
A simple solution to dynamically retrieve function names [like magic variables] is the use of scoped variables.
{
function parent() {
console.log(a.name);
}; let a = parent
}
{
function child() {
console.log(a.name)
}; let a = child
};
parent();//logs parent
child();//logs child
Note: Nested functions cease to be source elements, and are hence not hoisted.
Also, this technique cannot work with anonymous functions.
Just try Function.name
const func1 = function() {};
const object = {
func2: function() {}
};
console.log(func1.name);
// expected output: "func1"
console.log(object.func2.name);
// expected output: "func2"
look here: http://www.tek-tips.com/viewthread.cfm?qid=1209619
arguments.callee.toString();
seems to be right for your needs.
Easy way to get function name from within fuction you are running.
function x(){alert(this.name)};x()
you can use Error.stack to trace the function name and exact position of where you are in it.
See stacktrace.js

Named Function Expressions in IE, part 2

I asked this question a while back and was happy with the accepted answer. I just now realized, however, that the following technique:
var testaroo = 0;
(function executeOnLoad() {
if (testaroo++ < 5) {
setTimeout(executeOnLoad, 25);
return;
}
alert(testaroo); // alerts "6"
})();
returns the result I expect. If T.J.Crowder's answer from my first question is correct, then shouldn't this technique not work?
A very good question. :-)
The difference:
The difference between this and your detachEvent situation is that here, you don't care that the function reference inside and outside "the function" is the same, just that the code be the same. In the detachEvent situation, it mattered that you see the same function reference inside and outside "the function" because that's how detachEvent works, by detaching the specific function you give it.
Two functions?
Yes. CMS pointed out that IE (JScript) creates two functions when it sees a named function expression like the one in your code. (We'll come back to this.) The interesting thing is that you're calling both of them. Yes, really. :-) The initial call calls the function returned by the expression, and then all of the calls using the name call the the other one.
Modifying your code slightly can make this a bit clearer:
var testaroo = 0;
var f = function executeOnLoad() {
if (testaroo++ < 5) {
setTimeout(executeOnLoad, 25);
return;
}
alert(testaroo); // alerts "6"
};
f();
The f(); at the end calls the function that was returned by the function expression, but interestingly, that function is only called once. All the other times, when it's called via the executeOnLoad reference, it's the other function that gets called. But since the two functions both close over the same data (which includes the testaroo variable) and they have the same code, the effect is very like there being just one function. We can demonstrate there are two, though, much the way CMS did:
var testaroo = 0;
var f = function executeOnLoad() {
if (testaroo++ < 5) {
setTimeout(executeOnLoad, 0);
return;
}
alert(testaroo); // alerts "6"
// Alerts "Same function? false"
alert("Same function? " + (f === executeOnLoad));
};
f();
This isn't just some artifact of the names, either, there really are two functions being created by JScript.
What's going on?
Basically, the people implementing JScript apparently decided to process named function expressions both as function declarations and as function expressions, creating two function objects in the process (one from the "declaration," one from the "expression") and almost certainly doing so at different times. This is completely wrong, but it's what they did. Surprisingly, even the new JScript in IE8 continues this behavior.
That's why your code sees (and uses) two different functions. It's also the reason for the name "leak" that CMS mentioned. Shifting to a slightly modified copy of his example:
function outer() {
var myFunc = function inner() {};
alert(typeof inner); // "undefined" on most browsers, "function" on IE
if (typeof inner !== "undefined") { // avoid TypeError on other browsers
// IE actually creates two function objects: Two proofs:
alert(inner === myFunc); // false!
inner.foo = "foo";
alert(inner.foo); // "foo"
alert(myFunc.foo); // undefined
}
}
As he mentioned, inner is defined on IE (JScript) but not on other browsers. Why not? To the casual observer, aside from the two functions thing, JScript's behavior with regard to the function name seems correct. After all, only functions introduce new scope in Javascript, right? And the inner function is clearly defined in outer. But the spec actually went to pains to say no, that symbol is not defined in outer (even going so far as to delve into details about how implementations avoid it without breaking other rules). It's covered in Section 13 (in both the 3rd and 5th edition specs). Here's the relevant high-level quote:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
Why did they go to this trouble? I don't know, but I suspect it relates to the fact that function declarations are evaluated before any statement code (step-by-step code) is executed, whereas function expressions — like all expressions — are evaluated as part of the statement code, when they're reached in the control flow. Consider:
function foo() {
bar();
function bar() {
alert("Hi!");
}
}
When the control flow enters function foo, one of the first things that happens is that the bar function is instantiated and bound to the symbol bar; only then does the interpreter start processing the statements in foo's function body. That's why the call to bar at the top works.
But here:
function foo() {
var f;
f = function() {
alert("Hi!");
};
f();
}
The function expression is evaluated when it's reached (well, probably; we can't be sure some implementations don't do it earlier). One good reason the expression isn't (or shouldn't be) evaluated earlier is:
function foo() {
var f;
if (some_condition) {
f = function() {
alert("Hi (1)!");
};
}
else {
f = function() {
alert("Hi! (2)");
};
}
f();
}
...doing it earlier leads to ambiguity and/or wasted effort. Which leads to the question of what should happen here:
function foo() {
var f;
bar();
if (some_condition) {
f = function bar() {
alert("Hi (1)!");
};
}
else {
f = function bar() {
alert("Hi! (2)");
};
}
f();
}
Which bar gets called at the beginning? The way the specification authors chose to address that situation was to say that bar is not defined in foo at all, hence side-stepping the issue entirely. (It's not the only way they could have addressed it, but it seems to be the way they chose to do so.)
So how does IE (JScript) process that? The bar called at the beginning alerts "Hi (2)!". This, combined with the fact we know two function objects are created based on our other tests, is the clearest indication that JScript processes named function expressions as function declarations and function expressions, because that's exactly what is supposed to happen here:
function outer() {
bar();
function bar() {
alert("Hi (1)!");
}
function bar() {
alert("Hi (2)!");
}
}
There we have two function declarations with the same name. Syntax error? You'd think so, but it isn't. The specification clearly allows it, and says that the second declaration in source code order "wins." From Section 10.1.3 of the 3rd edition spec:
For each FunctionDeclaration in the code, in source text order, create a property of the variable object whose name is the Identifier in the FunctionDeclaration...If the variable object already has a property with this name, replace its value and attributes...
(The "variable object" is how symbols get resolved; that's a whole 'nother topic.) It's just as unambiguous in the 5th edition (Section 10.5), but, um, a lot less quotable.
So it's just IE, then?
Just to be clear, IE isn't the only browser that has (or had) unusual handling of NFEs, although they're getting pretty lonely (a pretty big Safari issue has been fixed, for instance). It's just that JScript has a really big quirk in this regard. But come to that, I think it actually is the only current major implementation with any really big issue — be interested to know of any others, if anyone knows of them.
Where we stand
Given all of the above, for the moment, I stay away from NFEs because I (like most people) have to support JScript. After all, it's easy enough to use a function declaration and then refer to it later (or indeed, earlier) with a variable:
function foo() { }
var f = foo;
...and that works reliably across browsers, avoiding issues like your detachEvent problem. Other reasonable people solve the problem differently, just accepting that two functions will get created and trying to minimize the impact, but I don't like that answer at all because of exactly what happened to you with detachEvent.
Well, it will work, the problem with JScript (IE), is that the identifier of the function expression (executeOnLoad) will leak to its enclosing scope, and actually creating two function objects..
(function () {
var myFunc = function foo () {};
alert(typeof foo); // "undefined" on all browsers, "function" on IE
if (typeof foo !== "undefined") { // avoid TypeError on other browsers
alert( foo === myFunc ); // false!, IE actually creates two function objects
}
})();

Categories