In-depth understanding of the JavaScript series (15) Functions (Functions)

Source: Internet
Author: User
Tags types of functions

Introduction
This section focuses on a very common ECMAScript object-function ), we will explain in detail how various types of functions affect context variable objects and what each function's scope chain contains, and answer questions such as the following: what are the differences between the functions declared below? (If so, what is the difference ).
Original article: http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/
Copy codeThe Code is as follows:
Var foo = function (){
...
};

Usual method:
Copy codeThe Code is as follows:
Function foo (){
...
}

Or, why should the following functions be enclosed in parentheses?
Copy codeThe Code is as follows:
(Function (){
...
})();

For more information, see Chapter 12 variable objects and chapter 14 scope chain. For more information, see the details of the two chapters.

But we still need to look at them one by one, starting with the function type:

Function Type
There are three function types in ECMAScript: function declaration, function expression, and function created by the function constructor. Each type has its own characteristics.
Function Declaration
Function declaration (FD) is a function:
There is a specific name
Position in the source code: either in the Program level or in the FunctionBody of other functions
Create in the context stage
Variable objects
The following statement
Copy codeThe Code is as follows:
Function exampleFunc (){
...
}

The main feature of this function type is that they only affect variable objects (that is, variable objects stored in the context VO ). This feature also explains the second important point (it is the result of the properties of the variable object) -- they are available in the code execution stage (because FD already exists in VO at the context stage-before code execution ).
For example, a function is called before it is declared)
Copy codeThe Code is as follows:
Foo ();
Function foo (){
Alert ('foo ');
}

Another key point is the second point in the above definition-the position of the function declaration in the source code:
Copy codeThe Code is as follows:
// The function can be declared as follows:
// 1) directly in the Global Context
Function globalFD (){
// 2) or in a function body
Function innerFD (){}
}

Only the two positions can declare the function, that is, it cannot be defined in the expression position or a code block.
Another method that can replace the function declaration is the function expression, which is interpreted as follows:
Function expression
Function expressions (FE) are such functions:
Must appear in the expression position in the source code
Available names
Does not affect variable objects
Create in code execution stage
The main feature of this function type is that it is always at the expression position in the source code. The simplest example is a value assignment statement:
Copy codeThe Code is as follows:
Var foo = function (){
...
};

In this example, an anonymous function expression is assigned to the variable foo. Then, the function can use the name foo to access -- foo ().
As described in the definition, function expressions can also have optional names:
Copy codeThe Code is as follows:
Var foo = function _ foo (){
...
};

Note that the external FE accesses -- foo () through the variable "foo", while the name "_ foo" may be used inside the function (such as recursive call ".
If FE has a name, it is difficult to distinguish it from FD. However, if you understand the definition, it is easy to distinguish: FE is always at the position of the expression. In the following example, we can see various ECMAScript expressions:
// Parentheses (grouping operators) can only be expressions
(Function foo (){});
// Only expressions can be used in the array initiator.
[Function bar () {}];
// You can only operate on expressions with commas (,).
1, function baz (){};
The expression definition indicates that FE can only be created in the code execution stage and does not exist in the variable object. Let's look at a sample behavior:
Copy codeThe Code is as follows:
// FE is unavailable before the definition phase (because it is created in the code execution phase)
Alert (foo); // "foo" undefined
(Function foo (){});
// It is not available after the definition phase, because it is not in the variable object VO
Alert (foo); // "foo" undefined
A considerable number of problems have emerged. Why do we need a function expression? The answer is obvious-use them in expressions, and "will not pollute" variable objects. The simplest example is to pass a function as a parameter to other functions.
Function foo (callback ){
Callback ();
}
Foo (function bar (){
Alert ('foo. bar ');
});
Foo (function baz (){
Alert ('foo. Baz ');
});

In the preceding example, FE is assigned a variable (that is, a parameter). The function stores the expression in the memory and accesses it through the variable name (because the variable affects the variable object ), as follows:
Copy codeThe Code is as follows:
Var foo = function (){
Alert ('foo ');
};
Foo ();

Another example is to create an encapsulated closure to hide auxiliary data from an external context (in the following example, we use FE, which is called immediately after creation ):
Copy codeThe Code is as follows:
Var foo = {};
(Function initialize (){
Var x = 10;
Foo. bar = function (){
Alert (x );
};
})();
Foo. bar (); // 10;
Alert (x); // "x" undefined

We can see that the function foo. bar (through the [Scope] attribute) accesses the internal variable "x" of the function initialize ". In addition, "x" cannot be accessed externally. In many libraries, this policy is often used to create "private" data and hide auxiliary entities. In this mode, the names of the initialized FE are usually ignored:
Copy codeThe Code is as follows:
(Function (){
// Initialize the scope
})();

Another example is to create FE using conditional statements during code execution without polluting the variable object VO.
Copy codeThe Code is as follows:
Var foo = 10;
Var bar = (foo % 2 = 0
? Function () {alert (0 );}
: Function () {alert (1 );}
);
Bar (); // 0

Parentheses
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.
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:
Copy codeThe Code is as follows:
Function (){
...
}();
// Even if there is a 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:
Copy codeThe Code is as follows:
// "Foo" is a function declaration, which is created when the context is entered.
Alert (foo); // Function
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:
Copy codeThe Code is as follows:
// Function declaration
Function foo (x ){
Alert (x );
}
// A grouping operator that contains an expression 1

(1 );
Copy codeThe Code is as follows:
// Another operator that contains a function expression
(Function (){});
// This operator also contains an expression "foo"
("Foo ");
// Etc.

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 ).
Copy codeThe Code is as follows:
(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.
Copy codeThe Code is as follows:
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:
Copy codeThe Code is as follows:
// Note that 1 is followed by the statement
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.
Implementation Extension: function statements
The following code should not be executed according to any of your function declarations:
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
(Function foo (bar ){
If (bar ){
Return;
}
Foo (true); // "foo" is available
})();
// Outside, it 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:
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
Object. prototype. x = 10;
Function foo (){
Var x = 20;
// Function declaration
Function bar (){
Alert (x );
}
Bar (); // 20, query from the foo variable object AO
// The same is true for anonymous function expressions.
(Function (){
Alert (x); // 20, which is also queried 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.
Copy codeThe Code is as follows:
Function foo (){
Var x = 10;
(Function bar (){
Alert (x); // 20, not 10, not obtained from the activity object of foo
// Search for "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.
Copy codeThe Code is as follows:
// FE is visible in the variable object
TestNFE ();
(Function testNFE (){
Alert ('testnfe ');
});
// FE can be seen after definition ends
// Just like function declaration
TestNFE ();

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.
Copy codeThe Code is as follows:
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 during execution
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.
Copy codeThe Code is as follows:
(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.
Copy codeThe Code is as follows:
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.
Copy codeThe Code is as follows:
Var foo = function bar (){
Alert (1 );
};
If (false ){
Foo = function bar (){
Alert (2 );
};
}
Bar (); // 2
Foo (); // 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.
Copy codeThe Code is as follows:
(Function (){
// If var is not used, it is not a variable in the current context.
// It is an attribute of the global object.
Foo = function foo (){};
})();
// However, the name foo is unavailable outside the anonymous function.
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.
Functions created using the function Constructor
Since this function object has its own characteristics, we distinguish it from FD and FE. The main feature of this function is that the [[Scope] attribute of this function only contains global objects:
Copy codeThe Code is as follows:
Var x = 10;
Function foo (){
Var x = 20;
Var y = 30;
Var bar = new Function ('alert (x); alert (y );');
Bar (); // 10, "y" undefined
}

We can see that the [[Scope] attribute of the function bar does not contain the Ao -- variable "y" in the foo context and the variable "x" is obtained from the Global object. By the way, the Function constructor can use either the new keyword or not. In this way, these variants are equivalent.

Other features of these functions are related to Equated Grammar Productions and Joined Objects. These mechanisms are provided as optimization suggestions (but optimization is not available in implementation. For example, if we have an array of 100 elements, the Joined Objects mechanism may be used for execution in a function loop. The result is that only one function object can be used for all elements in the array.

Copy codeThe Code is as follows:
Var a = [];

For (var k = 0; k <100; k ++ ){
A [k] = function () {}; // joined objects may be used
}

However, functions created through the function constructor are not connected.
Copy codeThe Code is as follows:
Var a = [];

For (var k = 0; k <100; k ++ ){
A [k] = Function (''); // It is always 100 different functions.
}

Another example related to joined objects:
Copy codeThe Code is as follows:
Function foo (){

Function bar (z ){
Return z * z;
}

Return bar;
}

Var x = foo ();
Var y = foo ();

The implementation here also has the right to connect object x and object y (using the same object), because functions (including their internal [Scope] attributes) basically, there is no difference. Therefore, functions created through the function constructor always require more memory resources.
Algorithm used to create a function
The following Pseudo Code describes the algorithm created by the function (except for the steps related to the Union object ). These descriptions help you understand more details about function objects in ECMAScript. This algorithm is suitable for all function types.
Copy codeThe Code is as follows:
F = new NativeObject ();

// Attribute [[Class] is "Function"
F. [[Class] = "Function"

// The prototype of the Function object is the prototype of the Function.
F. [[Prototype] = Function. prototype

// Medical to function itself
// Activate [[Call] when calling expression F
// Create a new execution Context
F. [[Call] = <reference to function>

// Compile in the object's common Constructor
// [[Construct] activated with the new Keyword
// Allocate memory to the new object
// Then Call F. [[Call] to initialize the new object passed as this.
F. [[Construct] = internalConstructor

// The scope chain of the current execution Context
// For example, create the context of F
F. [[Scope] = activeContext. Scope
// If a Function is created using new Function,
// Then
F. [[Scope] = globalContext. Scope

// Number of input parameters
F. length = countParameters

// The prototype created by F object
_ ObjectPrototype = new Object ();
_ ObjectPrototype. constructor = F // {DontEnum}. x cannot be enumerated in a loop.
F. prototype = _ objectPrototype

Return F

Note: F. [[Prototype] is a Prototype of a function (constructor. prototype is the prototype of the object created through this function (because the terms are often confusing, F. prototype is called the prototype of the constructor, which is incorrect ).

Conclusion
This article is somewhat long. However, as we will continue to discuss functions in the next section on objects and prototypes, I am also happy to answer your questions in the comments.

Other references
  • 13.-Function Definition;
  • 15.3-Function Objects.

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.