JavaScript function 3: function expressions in a group

Source: Internet
Author: User
Let's go back and answer the question mentioned at the beginning of the article-"Why must I enclose the circle in the call immediately after the function is created ?"

Let's go back and answer the question mentioned at the beginning of the article-"Why must parentheses be used to enclose the function immediately after it is created ?", The answer is: this is the limit for expression sentences.

(function () {  ...})();

According to the standard, an expression statement cannot start with a braces {because it is difficult to distinguish it from a code block. Similarly, it cannot start with a function keyword, because it is difficult to distinguish it from a function declaration. That is, if we define a function to be executed immediately, call it as follows immediately after it is created:

Function () {...} (); // even if the name function foo (){...}();

We use the function declaration. The two definitions mentioned above indicate that the interpreter will report errors during interpretation, but there may be multiple reasons.

If it is defined in the Global Code (that is, the program level), the interpreter will regard it as a function declaration, because it starts with the function keyword. In the first example, we will get the SyntaxError error, because the function declaration has no name (we mentioned earlier that the function declaration must have a name ).

In the second example, a function declaration named foo is created normally, but we still get a syntax error-the grouping operator error without any expressions. After the function declaration, it is indeed a grouping operator, rather than the parentheses used by a function call. So if we declare the following code:

// "Foo" is a function declaration that creates alert (foo) when entering the context; // function foo (x) {alert (x) ;}( 1 ); // This is just a grouping operator, not a function call! Foo (10); // This is a real function call. The result is 10.

The above code is correct because two objects are generated during the Declaration: A function declaration and a group operation with 1. The above example can be understood as the following code:

// Function declaration function foo (x) {alert (x) ;}// a grouping operator that contains an expression 1 (1); // another operator, contains a function expression (function () {}); // This operator also contains the expression "foo" ("foo"); // and so on.

If we define the following code (the definition contains a statement), we may say that the definition is ambiguous and an error is returned:

if (true) function foo() {alert(1)}

According to the specification, the above Code is incorrect (an expression statement cannot start with the function keyword), but the following example does not report an error. Why?

Let's tell the interpreter: I call it immediately after the function declaration. The answer is very clear. You have to declare the function expression instead of the function declaration, in addition, the simplest way to create an expression is to use the grouping operator parentheses, which always contains the expression. Therefore, the interpreter will not be ambiguous during interpretation. In the code execution phase, this function will be created, executed immediately, and then automatically destroyed (if not referenced ).

(Function foo (x) {alert (x) ;}) (1); // This is the call, not the grouping operator.

The above code is what we call in parentheses to enclose an expression and then call it through (1.

NOTE: For the following function to be executed immediately, parentheses are not required because the function is already in the position of the expression, the parser knows that it processes the FE that should be created during the function execution phase, so that the function is called immediately after the function is created.

var foo = {   bar: function (x) {    return x % 2 != 0 ? 'yes' : 'no';  }(1) }; alert(foo.bar); // 'yes'

As we can see, foo. bar is a string rather than a function. The function here is only used to initialize this attribute based on the condition parameter-it is created and called immediately.

Therefore, the complete answer to the question "Parentheses" is as follows: when a function is not in the position of an expression, the grouping operator parentheses are required-that is, the function is manually converted to FE. If the parser knows that it is processing FE, there is no need to use parentheses.

In addition to braces, functions can also be converted to the FE type in the following form, for example:

// Note that 1 is followed by declaration 1, function () {alert ('anonus us function is called') ;}(); // or this! Function () {alert ('ecmascript ') ;}(); // other manually converted forms...

In this example, parentheses are the most concise method.

By the way, the group expression can enclose the function description without calling parentheses or including calling parentheses. That is, the following two expressions are correct FE.

The following code should not be executed according to any function declaration:

If (true) {function foo () {alert (0) ;}} else {function foo () {alert (1) ;}} foo (); // 1 or 0? The actual test results in different environments are different.

It is worth noting that, according to the standard, This syntactic structure is usually incorrect, because we still remember that a function declaration (FD) it cannot appear in a code block (here, if and else contain code blocks ). We once said that FD only appears in two locations: Program level or directly in other function bodies.

This is incorrect because the code block only contains statements. The unique position of a function that can appear in a block is one of these statements-the expression statements discussed above. However, by definition it cannot start with braces (since it is different from a code block) or start with a function keyword (since it is different from FD ).

However, in the standard error handling section, it allows Extension Execution of the program syntax. One of these extensions is the functions that we see appear in the code block. In this example, all existing executions today will not throw an exception and will handle it. But they all have their own methods.

The emergence of if-else branch statements means a dynamic choice. That is, logically, it should be a function expression (FE) dynamically created during code execution ). However, most executions simply create a function declaration (FD) when entering the context stage, and use the final declared function. That is, the function foo will display "1". In fact, the else branch will never be executed.

However, SpiderMonkey (and TraceMonkey) treats this situation in two ways: on the one hand, it does not treat the function as a declaration (that is, the function is created according to conditions during code execution), but on the other hand, since there are no parentheses (A parsing error occurs again -- "different from FD"), they cannot be called, so they are not real function expressions and are stored in variable objects.

I personally think that the SpiderMonkey behavior in this example is correct, and it splits the intermediate type of its own function -- (FE + FD ). These functions are created at the appropriate time according to the conditions, unlike FE, like a FD that can be called externally. SpiderMonkey calls this syntax extension as a function Statement (FS ); this syntax is mentioned in MDC.

Features of name function expressions

When function expression FE has a name (named function expression, abbreviated as NFE), it has an important feature. From the definition (as we can see in the above example), we know that the function expression does not affect a context variable object (that means it is neither possible to call it by name before the function declaration, it is also impossible to call it after declaration ). However, FE can call itself by name in recursive calls.

(Function foo (bar) {if (bar) {return;} foo (true); // "foo" is available}) (); // outside, is unavailable foo (); // "foo" undefined

Where is "foo" stored? In the activity object of foo? No, because no "foo" is defined in foo ". Create foo in the context parent variable object? No, because according to the definition -- FE won't affect VO (variable object) -- we can actually see from the external call of foo. So where?

The following are the key points. When the interpreter encounters a named FE in the code execution phase, it creates a specific auxiliary object before FE is created and adds it to the front end of the current scope chain. Then it creates FE. At this time (as we know in chapter 4), the function obtains the [[Scope] Attribute-create the Scope chain of the function context ). Then, the FE name is added to a specific object as a unique attribute. The value of this attribute is referenced to FE. The last step is to remove the specific object from the parent scope chain. Let's look at this algorithm in the pseudo code:

SpecialObject ={}; Scope = specialObject + Scope; foo = new FunctionExpression; foo. [[Scope] = Scope; specialObject. foo = foo; // {DontDelete}, {ReadOnly} delete Scope [0]; // delete the defined special object specialObject from the Scope chain

Therefore, this name is unavailable outside the function (because it is not in the parent scope chain). However, a specific object is already stored in [[scope] of the function, the name is available.

However, note that some implementations (such as Rhino) do not store this optional name in the FE activation object rather than in a specific object. Execution in Microsoft completely breaks the FE rule. It maintains this name in the parent variable object so that functions become accessible externally.

NFE and SpiderMonkey

Let's take a look at the differences between NFE and SpiderMonkey. Some versions of SpiderMonkey have a property related to a specific object, which can be treated as a bug (although all of them are implemented as standard, but more like an ECMAScript standard bug ). It is related to the identifier parsing mechanism: the analysis of the scope chain is two-dimensional. In the identifier parsing, the prototype chain of each object in the scope chain is also considered.

If we define an attribute in Object. prototype and reference a variable named "nonexistent. We can see this execution mechanism. In this way, in the "x" Resolution in the following example, we will reach the global object, but no "x" is found ". However, in SpiderMonkey, The Global Object inherits the attributes in Object. prototype. Correspondingly, "x" can also be parsed.

Object.prototype.x = 10; (function () {  alert(x); // 10})();

The activity object has no prototype. According to the same starting condition, it is impossible to see such behavior of internal functions in the above example. If you define a local variable "x" and define an internal function (FD or anonymous FE), then reference "x" in the internal function ". This variable will be parsed in the context of the parent function (that is, where it should be parsed) rather than in Object. prototype.

Object. prototype. x = 10; function foo () {var x = 20; // function declaration function bar () {alert (x) ;}bar (); // 20, the same is true for querying // anonymous function expressions from the foo variable object AO (function () {alert (x); // 20, also from the foo variable object AO }) ();} foo ();

However, some executions have exceptions, and it sets a prototype for the activity object. Therefore, in Blackberry execution, the "x" in the above example is parsed as "10 ". That is to say, since the foo value has been found in Object. prototype, it will not reach the active Object of foo.

AO(bar FD or anonymous FE) -> no ->AO(bar FD or anonymous FE).[[Prototype]] -> yes - 10

In SpiderMonkey, we can see the same situation in the specific object named FE. This specific Object (according to the standard) is a common Object -- "just like the expression new Object ()". Correspondingly, it should start from the Object. prototype inherits attributes. This is exactly what we see in SpiderMonkey (version 1.7 or later. Other executions (including the new TraceMonkey) do not set a prototype for a specific object.

Function foo () {var x = 10; (function bar () {alert (x); // 20, not 10, not obtained from the foo activity object // "x" from the chain: // AO (bar)-no-> _ specialObject (bar) -> no // _ specialObject (bar ). [[Prototype]-yes: 20}) ();} Object. prototype. x = 20; foo ();
NFE and Jscript

Currently, many built-in JScript execution bugs related to function expressions (NFE) exist in IE browsers (until JScript 5.8-IE8. All of these bugs are totally in conflict with ECMA-262-3 standards; some may cause serious errors.

In this example, JScript destroys the main rules of FE and should not be stored in variable objects by function names. Optional FE names should be stored in specific objects and can only be accessed in the function itself (rather than elsewhere. But IE directly stores it in the parent variable object. In addition, FE is treated as a function declaration (FD) In JScript. It is created at the context stage and can be accessed before the definition in the source code.

// FE can be seen in the variable object testNFE (); (function testNFE () {alert ('testnfe ');}); // FE can be seen after definition ends. // testNFE () is like function declaration ();

As we can see, it completely violates the rules.

Second, when FE is assigned to a variable in the declaration, JScript creates two different function objects. Logically (especially outside NFE, the name should not be accessed at all) It is difficult to name such behavior.

Var foo = function bar () {alert ('foo') ;}; alert (typeof bar); // "function ", // interestingly, alert (foo = bar); // false! Foo. x = 10; alert (bar. x); // undefined // but the result is the same as foo (); // "foo" bar (); // "foo"

Again, we can see that it is a mess. However, it should be noted that, if it is separated from the value assignment of the variable, NFE is described separately (for example, through the group operator), then it is assigned to a variable and its equality is checked, the result is true, which is like an object.

(function bar() {}); var foo = bar;alert(foo === bar); // true foo.x = 10;alert(bar.x); // 10

This can be explained. In fact, create two objects again, but in fact keep one. If we think that the NFE here is treated as FD again, then create the FD bar in the context phase. After that, the second object-function expression (FE) bar in the code execution stage is created and will not be stored. Correspondingly, there is no reference to FE bar and it is removed. In this way, there is only one object-FD bar, and its reference is assigned to the variable foo.

Third, when using arguments. callee to indirectly reference a function, it references the name of the activated object (specifically -- here there are two function objects.

var foo = function bar() {  alert([    arguments.callee === foo,    arguments.callee === bar  ]);}; foo(); // [true, false]bar(); // [false, true]

Fourth, JScript treats NFE like a common FD and does not obey the rules of conditional expressions. That is, like a FD, NFE is created when it enters the context, and the final definition in the code is used.

var foo = function bar() {  alert(1);}; if (false) {  foo = function bar() {    alert(2);  };}bar(); // 2foo(); // 1

This behavior can be explained logically. At the context stage, the FD bar is created, that is, the function containing alert (2. Later, in the code execution phase, a new function, FE bar, was created and its reference was assigned to the variable foo. In this way, foo activation generates alert (1 ). The logic is clear, but considering the IE bug, since the execution is obviously broken and dependent on the JScript bug, I added quotation marks to the word "logically (logically.

The fifth bug in JScript is related to the creation of global object attributes. A global object is generated by assigning a value to an undefined identifier (that is, there is no var keyword. Since NFE is treated as FD here, it is stored in the variable object and assigned to an undefined identifier (that is, it is not assigned to a variable but a common attribute of the Global Object ), in case the function name is the same as the undefined identifier, this attribute is not global.

(Function () {// without var, it is not a variable in the current context. // It is a global object attribute foo = function foo (){};}) (); // However, outside the anonymous function, the name foo is unavailable alert (typeof foo); // undefined

"Logic" is clear: in the context stage, function declaration foo gets the active object of the anonymous function in the local context. In the code execution stage, the name foo already exists in AO, that is, it is used as a local variable. Correspondingly, in the assignment operation, simply update the property foo that already exists in AO, instead of creating a new property for the global object according to the logic of the ECMA-262-3.

Additional reading

The topic list of this article is as follows:

  1. How should we understand the working principle of the JavaScript engine?
  2. JavaScript exploration: the importance of writing maintainable code
  3. JavaScript exploration: exercise caution when using global variables
  4. JavaScript exploration: var pre-parsing and side effects
  5. JavaScript exploration: for Loop (for Loops)
  6. JavaScript exploration: for-in loop (for-in Loops)
  7. Exploring JavaScript: Prototypes is too powerful
  8. JavaScript: eval () is the devil"
  9. JavaScript exploration: Using parseInt () for Numerical Conversion
  10. Exploring JavaScript: Basic coding specifications
  11. JavaScript exploration: function declaration and function expression
  12. JavaScript exploration: Name function expressions
  13. JavaScript: function name in the debugger
  14. JavaScript: JScript Bug
  15. JavaScript exploration: Memory Management of JScript
  16. Exploring JavaScript: SpiderMonkey's quirks
  17. JavaScript exploration: an alternative solution to naming function expressions
  18. JavaScript exploration: Object
  19. JavaScript exploration: Prototype chain
  20. JavaScript exploration: Constructor
  21. JavaScript probing: executable context Stack
  22. Execution context 1: Variable object and activity object
  23. Execution context 2: Scope chain Scope Chains
  24. Execution context 3: Closure Closures
  25. Execution context 4: This pointer
  26. Exploring JavaScript: Powerful prototype and prototype chain
  27. JavaScript Functions 1: function declaration
  28. JavaScript function 2: function expressions
  29. JavaScript function 3: function expressions in a group
  30. JavaScript function 4: function Constructor
  31. JavaScript variable object 1: VO Declaration
  32. JavaScript variable object 2: VO in different execution contexts
  33. JavaScript variable object 3: two stages of execution Context
  34. JavaScript variable object IV: Variables
  35. Property of the JavaScript variable object __parent _
  36. JavaScript scope chain 1: Scope chain Definition
  37. JavaScript scope chain 2: function Lifecycle
  38. JavaScript scope chain 3: Scope chain features
  39. JavaScript closure 1: Introduction to closures
  40. JavaScript closure 2: Implementation of closure
  41. JavaScript closure 3: Closure usage

This article is available at http://www.nowamagic.net/librarys/veda/detail/1664.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.