Two functions with the same name in JavaScript - how can this work?
As far as I know, function foo() { aaa(); }
is just var foo = function(){ aaa() }
in JavaScript.开发者_运维技巧 So adding function foo() { bbb(); }
should either overwrite the foo
variable, or ignore the second definition - that's not the point. The point is that there should be one variable foo
.
So, in this example the me
variable should not be correctly resolved from inside the methods and it is not in Explorer 8 :-). I came to this example by trying to wrap them into another closure where (var
) me
would be, but I was surprised that it's not necessary:
var foo = {
bar1 : function me() {
var index = 1;
alert(me);
},
bar2 : function me() {
var index = 2;
alert(me);
}
};
foo.bar1(); // Shows the first one
foo.bar2(); // Shows the second one
Demo: http://jsfiddle.net/W5dqy/5/
AFAIK function foo() { aaa(); } is just var foo = function(){ aaa() } in JavaScript.
Not quite; they're similar, but also quite different. JavaScript has two different but related things: Function declarations (your first example there), and function expressions (your second, which you then assign to a variable). They happen at different times in the parsing cycle and have different effects.
This is a function declaration:
function foo() {
// ...
}
Function declarations are processed upon entry into the enclosing scope, before any step-by-step code is executed.
This is a function expression (specifically, an anonymous one):
var foo = function() {
// ...
};
Function expressions are processed as part of the step-by-step code, at the point where they appear (just like any other expression).
Your quoted code is using a named function expression, which look like this:
var x = function foo() {
// ...
};
(In your case it's within an object literal, so it's on the right-hand side of an :
instead of an =
, but it's still a named function expression.)
That's perfectly valid, ignoring implementation bugs (more in a moment). It creates a function with the name foo
, doesn't put foo
in the enclosing scope, and then assigns that function to the x
variable (all of this happening when the expression is encountered in the step-by-step code). When I say it doesn't put foo
in the enclosing scope, I mean exactly that:
var x = function foo() {
alert(typeof foo); // alerts "function" (in compliant implementations)
};
alert(typeof foo); // alerts "undefined" (in compliant implementations)
Note how that's different from the way function declarations work (where the function's name is added to the enclosing scope).
Named function expressions work on compliant implementations. Historically, there were bugs in implementations (early Safari, IE8 and earlier). Modern implementations get them right, including IE9 and up. (More here: Double take and here: Named function expressions demystified.)
So, in this example the
me
variable shoudl not be corectly resolved from inside the methods
Actually, it should be. A function's true name (the symbol between function
and the opening parenthesis) is always in-scope within the function (whether the function is from a declaration or a named function expression).
NOTE: The below was written in 2011. With the advances in JavaScript since, I no longer feel the need to do things like the below unless I know I'm going to be dealing with IE8 (which is very rare these days).
Because of implementation bugs, I used to avoid named function expressions. You can do that in your example by just removing the me
names, but I prefer named functions, and so for what it's worth, here's how I used to write your object:
var foo = (function(){
var publicSymbols = {};
publicSymbols.bar1 = bar1_me;
function bar1_me() {
var index = 1;
alert(bar1_me);
}
publicSymbols.bar2 = bar2_me;
function bar2_me() {
var index = 2;
alert(bar2_me);
}
return publicSymbols;
})();
(Except I'd probably use a shorter name than publicSymbols
.)
Here's how that gets processed:
- An anonymous enclosing function is created when the
var foo = ...
line is encountered in the step-by-step code, and then it is called (because I have the()
at the very end). - Upon entry into the execution context created by that anonymous function, the
bar1_me
andbar2_me
function declarations are processed and those symbols are added to the scope inside that anonymous function (technically, to the variable object for the execution context). - The
publicSymbols
symbol is added to the scope inside the anonymous function. (More: Poor misunderstoodvar
) - Step-by-step code begins by assigning
{}
topublicSymbols
. - Step-by-step code continues with
publicSymbols.bar1 = bar1_me;
andpublicSymbols.bar2 = bar2_me;
, and finallyreturn publicSymbols;
- The anonymous function's result is assigned to
foo
.
These days, though, unless I'm writing code I know needs to support IE8 (sadly, as I write this in November 2015 it still has significant global market share, but happily that share is plummetting), I don't worry about it. All modern JavaScript engines understand them just fine.
You can also write that like this:
var foo = (function(){
return {
bar1: bar1_me,
bar2: bar2_me
};
function bar1_me() {
var index = 1;
alert(bar1_me);
}
function bar2_me() {
var index = 2;
alert(bar2_me);
}
})();
...since those are function declarations, and thus are hoisted. I don't usually do it like that, as I find it easier to do maintenance on large structures if I do the declaration and the assignment to the property next to each other (or, if not writing for IE8, on the same line).
Both me
lookups, are only visible/available inside the function expression.
Infact those two are named function expressions, and the ECMAscript specification tells us, that the name of an expression is not exposed to the such called Variable object
.
Well I tried to put that only in a few words, but while trying to find the right words, this ends up in pretty deep chain of ECMAscript behavior. So, function expression
are not stored in a Variable
/ Activation Object
. (Would lead to the question, who those guys are...).
Short: Every time a function is called, a new Context
is created. There is some "blackmagic" kind of guy that is called, Activation object
which stores some stuff. For instance, the
- arguments of the function
- the [[Scope]]
- any variables created by
var
For instance:
function foo(test, bar) {
var hello = "world";
function visible() {
}
(function ghost() {
}());
}
The Activation Object for foo
would look like:
- arguments: test, bar
- variables: hello (string), visible (function)
- [[Scope]]: (possible parent function-context), Global Object
ghost
is not stored in the AO! it would just be accesssible under that name within the function itself. While visible()
is a function declaration (or function statement) it is stored in the AO. This is because, a function declaration is evaluated when parsing and function expression is evaluated at runtime.
What happens here is that function()
has many different meanings and uses.
When I say
bar1 : function me() {
}
then that's 100% equivalent to
bar1 : function() {
}
i.e. the name doesn't matter when you use function to assign the variable bar1
. Inside, me
is assigned but as soon as the function definition is left (when you assign bar2
), me
is created again as a local variable for the function definition that is stored in bar2
.
精彩评论