The JavaScript -- Item7 function and (name) function expressions you don't know
1. function declaration and function expression
In ECMAScript, the two most common methods for creating a function are function expressions and function declarations. The difference between the two methods is a bit dizzy, because the ECMA specification only makes it clear: the function declaration must contain an Identifier (the name of a function that is commonly used), while the function expression can omit this Identifier:
Function declaration:
Function Name (parameter: OPTIONAL) {function body}
Function expression:
Function Name (optional) (parameter: OPTIONAL) {function body}
Therefore, we can see that if the function name is not declared, it must be an expression. If the function name is declared, how can we determine whether it is a function declaration or a function expression? ECMAScript is distinguished by context. If function foo () {} is a part of the value assignment expression, it is a function expression. If function foo () {} is contained in a function, or at the top of the program, it is a function declaration.
Function foo () {}// declaration, because it is part of the program var bar = function foo () {}; // expression, because it is a part of the value assignment expression new function bar () {}; // expression, because it is a new expression (function () {function bar () {}// declaration, because it is part of the function body })();
There are very subtle differences between expressions and declarations. First, the function declaration will be parsed and evaluated before any expression is parsed and evaluated, even if your declaration is on the last line of the Code, it will also be parsed/evaluated before the first expression in the same scope. For example, the fn function is declared after alert, but when alert is executed, fn has been defined:
alert(fn());function fn() { return 'Hello world!';}
In addition, you need to note that function declaration can be used in conditional statements but is not standardized. That is to say, different environments may have different execution results. In this case, it is best to use a function expression:Because there is no block-level scope concept in the Condition Statement.
// Never do this! // Some browsers return the first function, while some browsers return the second if (true) {function foo () {return 'first ';}} else {function foo () {return 'second';} foo (); // opposite. In this case, we use the function expression var foo; if (true) {foo = function () {return 'first ';};} else {foo = function () {return 'second' ;};} foo ();
The actual rules of the function declaration are as follows:
Function declarations can only appear in programs or functions. Syntactically, they cannot appear in blocks (blocks )({... }), For example, cannot appear in if, while, or for statements. Because the Block can only contain Statement statements, but cannot contain source elements such as function declaration. On the other hand, if you take a closer look at the rule, you will also find that the only possible reason for the expression to appear in the Block is to make it part of the expression statement. However, the specification clearly stipulates that expression statements cannot start with a function keyword. In fact, function expressions cannot appear in Statement statements or blocks (because blocks are composed of Statement statements ).
2. Name function expression
When it comes to the naming function expression, it must have a name. In the preceding example, var bar = function foo () {}; is a valid naming function expression, but remember one thing: this name is valid only in the scope of the newly defined function, because the specification stipulates that the identifier cannot be valid in the peripheral scope:
Var f = function foo () {return typeof foo; // function ---> foo is valid within the internal scope}; // foo is used externally as an invisible typeof foo; // undefinedf (); // function
In this case, what is the use of the name function expression? Why?
As we said at the beginning: to give it a name is to make the debugging process more convenient, because during debuggingCall StackEach item in has its own name to describe, so the debugging process is so cool and the feeling is different.
Tips:Here is a small question: In ES3, the scope Object of the name function expression also inherits the attribute of Object. prototype. This means that only the name of the function expression will introduce all attributes in Object. prototype into the scope. The results may be unexpected.
Var constructor = function () {return null;} var f = function f () {return construcor () ;}f (); // {in ES3 environment}
This program seems to generate null, but will actually generate a new object. Because the name function expression inherits Object. prototype. constructor (the Object constructor) within its scope ). Like the with statement, this scope will be affected by the dynamic change of Object. prototype. Fortunately, ES5 corrected this error.
A reasonable solution to this behavior is to create a local variable with the same name as the function expression and assign a value of null. Even in an environment where the function expression declaration is not mistakenly promoted, using var to re-declare the variable ensures that the variable g is still bound. Setting the variable g to null ensures that repeated functions can be recycled.
var f = function g(){ return 17;}var g =null;
3. Name function expressions in the debugger (call stack)
As I said just now, the real use of the name function expression is debugging. How can I use it? If a function has a name, the debugger displays its name on the called Stack during debugging. Some debuggers (Firebug) Sometimes name your functions and display them to share the same role with the convenience of applying them, but normally, these debuggers only install simple rules to name them, so there is not much value. Let's look at an example: there is no need to name a function expression.
Function foo () {return bar ();} function bar () {return baz ();} function baz () {debugger;} foo (); // here we use three function declarations with names. // when the debugger goes to the debugger statement, the Firebug call stack looks very clear. // The call stack name bazbarfooexpr_test.html () is clearly displayed ()
By viewing the call stack information, we can clearly understand that foo calls the bar, barand calls baz(and fooben is called in the global scope of the expr_test.html document). However, there is a better place, this is the function that Firebug named anonymous expressions:
Function foo () {return bar ();} var bar = function () {return baz ();} function baz () {debugger;} foo (); // Call stackbazbar () // have you seen it? Fooexpr_test.html ()
Then, when the function expression is a little more complex, the debugger is not so intelligent. We can only see the question mark in the call Stack:
Function foo () {return bar ();} var bar = (function () {if (window. addEventListener) {return function () {return baz () ;}}else if (window. attachEvent) {return function () {return baz () ;}}) (); function baz () {debugger;} foo (); // Call stackbaz (?) () // The question mark is displayed as an anonymous function (anonymous functionfunctions fooexpr_test.html ()
In addition, when a function is assigned to multiple variables, the following depressing problems also occur:
function foo(){ return baz();}var bar = function(){ debugger;};var baz = bar;bar = function() { alert('spoofed');};foo();// Call stack:bar()fooexpr_test.html()
At this time, the call stack shows that foo calls bar, but this is not the case. The reason for this problem is that baz and another one contains alert ('spoofed ') the function is caused by reference swap.
In the final analysis, only getting a name for a function expression is the most delegate method, that is, using a namefunction expression. We will use an expression with a name to rewrite the above example (note that the names of the two functions returned in the expression block that is called immediately are bar ):
Function foo () {return bar ();} var bar = (function () {if (window. addEventListener) {return function bar () {return baz () ;};} else if (window. attachEvent) {return function bar () {return baz () ;}}) (); function baz () {debugger;} foo (); // The call stack information is displayed again! Bazbarfooexpr_test.html ()
OK. I learned another trick, right?