Introduced
In this chapter, we'll focus on a very common ECMAScript object--function--and we'll explain in detail how various types of functions affect the context's variable objects and what the scope chain of each function contains. and answer questions like the following: What's the difference between the functions declared below? (If so, what the difference is).
Original: http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/
Copy Code code as follows:
var foo = function () {
...
};
Usual way of doing it:
Copy Code code as follows:
Or, why should the following function be enclosed in parentheses?
Copy Code code as follows:
For a specific introduction, the early 12 variable objects and 14 chapter scope chain are described, if you need to know more about the content, please check the above 2 chapters.
But we still have to look at one by one separately, starting with the type of the function:
function type
There are three types of functions in ECMAScript: function declarations, function expressions, and functions created by function constructors. Each one has its own characteristics.
function Declaration
A function declaration (abbreviated to FD) is one such function:
Have a specific name
Location in source code: either at the program level or in the body of other functions (Functionbody)
Created in the context phase
affect variable objects
Declare in the following manner
Copy Code code as follows:
function Examplefunc () {
...
}
The main feature of this type of function is that they only affect variable objects (that is, variable objects stored in Vo in the context). This feature also explains the second important point (which is the result of variable object attributes)--which is already available in the Code execution phase (since FD already exists in VO in the context phase)-before the code executes.
For example (the function is invoked before its declaration)
Copy Code code as follows:
Foo ();
function foo () {
Alert (' foo ');
}
Another key point of knowledge is the 2nd in the above definition-the position of the function declaration in the source code:
Copy Code code as follows:
Functions can be declared in the following places:
1) directly in the global context
function Globalfd () {
2) or in the function of a function body
function Innerfd () {}
}
Only these 2 positions can declare a function, which means that it is not possible to define it in an expression position or in a code block.
Another way to replace a function declaration is to function an expression, which is explained as follows:
function expression
A function expression (abbreviated to FE) is a function of this type:
In the source code must appear in the position of the expression
Have an optional name
Variable objects are not affected
Create in code execution phase
The main feature of this type of function is that it is always in the position of an expression in the source code. One of the simplest examples is an assignment declaration:
Copy Code code as follows:
var foo = function () {
...
};
This example shows that an anonymous function expression is assigned to the variable foo, and then the function can access--foo () using the name Foo.
As described in the definition, a function expression can also have an optional name:
Copy Code code as follows:
var foo = function _foo () {
...
};
It is to be noted that the external FE accesses--foo () through the variable "foo", while the name "_foo" may be used within the function (such as recursive invocation).
If FE has a name, it is difficult to distinguish it from FD. But if you understand the definition, the distinction is simple: FE is always in the position of an expression. In the following example we can see various ECMAScript expressions:
Parentheses (group operators) can only be an expression
(function foo () {});
Can only be an expression within an array initializer
[function Bar () {}];
Commas can only manipulate expressions
1, function Baz () {};
Description in the expression definition: Fe can only be created during code execution and does not exist in variable objects, let's take a look at the following example:
Copy Code code as follows:
Fe is not available until the definition phase (because it was created during code execution phase)
Alert (foo); "Foo" is not defined
(function foo () {});
is not available after the definition phase because he is not in the variable object vo
Alert (foo); "Foo" is not defined
Quite a bit of the problem arises, why do we need a function expression? The answer is obvious--use them in an expression, "do not pollute" variable objects. The simplest example is to pass a function as an argument to another function.
function Foo (callback) {
Callback ();
}
Foo (function bar () {
Alert (' Foo.bar ');
});
Foo (function Baz () {
Alert (' Foo.baz ');
});
In the example above, the FE assignment gives a variable (that is, a parameter) that the function saves in memory and accesses through the variable name (because the variable affects the variable object) as follows:
Copy Code code as follows:
var foo = function () {
Alert (' foo ');
};
Foo ();
Another example is the creation of encapsulated closures that hide the supporting data from an external context (in the following example we use FE, which is invoked immediately after creation):
Copy Code code as follows:
var foo = {};
(function Initialize () {
var x = 10;
Foo.bar = function () {
alert (x);
};
})();
Foo.bar (); 10;
alert (x); "X" Not defined
We see the function foo.bar (through [[[Scope]] property) to the internal variable "x" of the function initialize. Also, "X" cannot be accessed directly from the outside. In many libraries, this strategy is often used to create "private" data and to hide secondary entities. In this mode, the name of the initialized Fe is usually ignored:
Copy Code code as follows:
(function () {
Initializing scopes
})();
Another example is the creation of FE through conditional statements during the code execution phase without polluting the variable object vo.
Copy Code code as follows:
var foo = 10;
var bar = (foo% 2 = 0
? function () {alert (0);}
: function () {alert (1);}
);
Bar (); 0
Questions about parentheses
Let's go back and answer the question that was mentioned at the beginning of the article-"Why must surround it with parentheses in an immediate call after a function is created?" The answer is this: that's the limit of the expression sentence.
By standard, an expression statement cannot start with a curly brace {because it is difficult to distinguish it from a block of code, and he cannot start with a function keyword because it is difficult to distinguish it from a function declaration. That is, so if we define a function that executes immediately, it is invoked as follows immediately after its creation:
Copy Code code as follows:
function () {
...
}();
Even if there is a name
function foo () {
...
}();
We use the function declaration, the above 2 definitions, the interpreter in the interpretation of the error will be, but there may be several reasons.
If you define it in the global code (that is, the program level), the interpreter will consider it as a function declaration because he starts with the functions keyword, and in the first case we 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, we have a function declaration called Foo that is normally created, but we still get a syntax error--there is no group operator error for any of the expressions. After the function declaration he is really a grouping operator, not a function call using parentheses. So if we declare the following code:
Copy Code code as follows:
"Foo" is a function declaration that 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, and the result is 10.
The above code is not problematic because the declaration produced 2 objects: a function declaration, a grouping operation with 1, the example above can be understood as the following code:
Copy Code code as follows:
function declaration
function foo (x) {
alert (x);
}
A grouping operator that contains an expression of 1
(1);
Copy Code code as follows:
Another operator that contains a function expression
(function () {});
In this operator, the expression "foo" is also included.
("foo");
Wait a minute
If we define a code like this (which contains a statement in the definition), we might say that the definition of ambiguity would be an error:
if (true) function foo () {alert (1)}
According to the specification, the code above is wrong (an expression statement cannot begin with a function keyword), but the following example does not have an error, think about why?
If we were to tell the interpreter that I was called immediately after the function declaration, the answer is very clear, you have to declare the function expression functions expression, rather than function declaration functions declaration, And the simplest way to create an expression is to use the grouping operator bracket, which is always an expression, so the interpreter doesn't get ambiguous when it interprets it. In the code execution phase, the function is created and executed immediately, and then automatically destroyed (if no reference is made).
Copy Code code as follows:
(function foo (x) {
alert (x);
}) (1); This is the call, not the grouping operator.
The code above is what we call an expression enclosed in parentheses and then passed (1).
Note that the surrounding parentheses are not necessary because the function is already in the position of the expression, and the parser knows that it handles the FE that should be created at the function execution stage, so that the function is called immediately after the function is created.
Copy Code code 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, where the function is used only to initialize the property based on the condition parameter-it is created and called immediately.
Therefore, the complete answer to the question about parentheses is as follows: When a function is not in the position of an expression, the parentheses of the grouping operator are necessary-that is, to convert the function to FE by hand.
If the parser knows that it is dealing with Fe, there is no need to use parentheses.
In addition to curly braces, the following form can also convert a function to a FE type, for example:
Copy Code code as follows:
Note that the 1, the following statement
1, function () {
Alert (' anonymous function is called ');
}();
or this.
!function () {
Alert (' ECMAScript ');
}();
Other forms of manual conversion
...
In this case, however, parentheses are the simplest way.
By the way, the group expression bounding function description can have no call to parentheses, or it can contain call parentheses, that is, the following two expressions are all correct fe.
Implementing Extensions: Function statements
The following code, according to any of your function declarations, should not be executed:
Copy Code code as follows:
if (true) {
function foo () {
Alert (0);
}
} else {
function foo () {
Alert (1);
}
}
Foo (); 1 or 0? The actual test in different environments shows that the results are not the same
It is important to note here that, by standard, this syntactic structure is usually incorrect, as we remember that a function declaration (FD) cannot appear in a block of code (where if and else contains a block of code). As we have said, FD only occurs in two locations: program levels or directly in other function bodies.
This is not true because the code block contains only statements. The only place in a function that can appear in a block is one of those statements-the expression statement that has been discussed above. However, by definition it cannot start with curly braces (since it is different from a code block) or starts with a function keyword (since it is different from FD).
However, in the standard error-handling section, it allows extended execution of program syntax. One such extension is the function that we see appearing in the code block. In this example, all existing executions of today do not throw an exception and will handle it. But they all have their own way.
The presence of a If-else branch statement means a dynamic choice. That is, logically, it should be a function expression (FE) that is created dynamically during the code execution phase. However, most executions simply create function declarations (FD) as they enter the context phase and use the last declared function. That is, function Foo will display "1″, in fact the Else branch will never be executed."
However, SpiderMonkey (and Tracemonkey) treats the situation in two ways: on the one hand, it does not treat functions as declarations (that is, functions are created according to conditions during code execution), but on the other hand, since there is no parentheses around (again a parse error-"Unlike FD"), They can't be called, so it's not really a function expression, it's stored in a variable object.
I personally think that the behavior of SpiderMonkey in this example is correct, splitting the middle type of its own function-(FE+FD). These functions are created at the right time, depending on the condition, unlike Fe, which is like a fd,spidermonkey that can be invoked from outside to call this syntax extension a function statement (abbreviated as FS), which is mentioned in the MDC.
Attributes of a named function expression
An important feature occurs when a function expression Fe has a name (called a named function expression, abbreviated to NFE). From the definition (as we see from the example above), we know that a function expression does not affect a variable object of a context (that means it is neither possible to call it by name before a function declaration, nor can it be invoked after the declaration). However, FE can call itself by name in a recursive call.
Copy Code code as follows:
(function foo (bar) {
if (bar) {
Return
}
Foo (true); "Foo" is available
})();
On the outside, is not available
Foo (); "Foo" is not defined
Where is "foo" stored? In the active object of Foo? No, because no "foo" is defined in Foo. Create Foo in the parent variable object of the context? Nor is it because by definition--fe does not affect VO (variable object)-call Foo from the outside we can actually see. So where is it?
Here are the key points. When the interpreter encounters a named Fe during code execution, it creates a secondary specific object and adds it to the front end of the current scope chain before the FE is created. Then it creates the Fe, at which point (as we know in the fourth scope chain) The function gets the [[Scope]] Property--The scope chain that created the function context. After that, the name of the FE is added to a specific object as a unique property, and the value of this property is referenced to the FE. The final step is to remove that particular object from the parent scope chain. Let's look at this algorithm in the pseudo code:
Copy Code code as follows:
Specialobject = {};
Scope = Specialobject + scope;
foo = new Functionexpression;
Foo. [[scope]] = Scope;
Specialobject.foo = foo; {Dontdelete}, {ReadOnly}
Delete Scope[0]; Remove a defined special object from the scope chain Specialobject
Therefore, this name is not available outside of the function (because it is not in the parent scope chain), but a particular object is already stored in the function's [[scope]], where the name is available.
It should be noted, however, that some implementations, such as Rhino, do not store this optional name in a particular object but in a Fe activation object. Execution in Microsoft completely breaks the FE rule by keeping the name in the parent variable object so that the function becomes accessible externally.
NFE and SpiderMonkey
Let's look at the difference between NFE and SpiderMonkey, and some versions of SpiderMonkey have a property associated with a particular object that can be treated as a bug (though all of the criteria are implemented as it is, but more like a bug on a ECMAScript standard). It is related to the parsing mechanism of identifiers: the analysis of the scope chain is two-dimensional, and in the parsing of identifiers, it also takes into account the prototype chain of each object in the scope chain.
If we define an attribute in Object.prototype and reference a variable that does not exist (nonexistent). We can see this implementation mechanism. Thus, in the "X" parsing of the example below, we will reach the global object, but no "x" is found. However, in SpiderMonkey the global object inherits the attributes in the Object.prototype, and "X" can be parsed accordingly.
Copy Code code as follows:
object.prototype.x = 10;
(function () {
alert (x); 10
})();
The active object has no prototype. According to the same starting condition, in the above example, it is not possible to see this behavior of the intrinsic function. If you define a local variable "x" and define an internal function (FD or an anonymous Fe), then refer to "X" in the internal function. This variable will be parsed in the parent function context (that is, where it should be parsed) rather than in Object.prototype.
Copy Code code as follows:
object.prototype.x = 10;
function foo () {
var x = 20;
function declaration
function Bar () {
alert (x);
}
Bar (); 20, query from Foo's variable object ao
The same is true of anonymous function expressions
(function () {
alert (x); 20, also from the variable object in Foo AO query
})();
}
Foo ();
However, some executions have exceptions, which set a prototype for the active object. Therefore, in the implementation of the BlackBerry, "X" in the example above is resolved to "10″." That is, since the value of Foo has been found in the 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, the same situation can be seen in the particular object named Fe. This particular object (as the standard) is a normal object-"Just like the expression new object ()" and, correspondingly, it should inherit the property from the Object.prototype, which is exactly what we see in SpiderMonkey (more than 1.7 versions). The rest of the execution (including the new Tracemonkey) does not set a prototype for a particular object.
Copy Code code as follows:
function foo () {
var x = 10;
(function bar () {
alert (x); 20, not 10, not from the active object of Foo
"X" looks up from the chain:
AO (BAR)-No-> __specialobject (bar)-> No
__specialobject (BAR). [[Prototype]]-yes:20
})();
}
object.prototype.x = 20;
Foo ();
NFE and JScript
There are many bugs associated with function expressions (NFE) in JScript execution that is built into the current IE browser (until JScript 5.8-ie8). All of these bugs are completely contradictory to the ecma-262-3 standard, and some can cause serious errors.
First, in this example, JScript destroys the primary rule of Fe, which should not be stored in a variable object through the function name. The optional FE name should be stored in a particular object and can only be accessed in the function itself, not elsewhere. However, IE stores it directly in the parent variable object. In addition, the named FE is treated as a function declaration (FD) in JScript. This is created at the stage of entering the context and can be accessed before the definition in the source code.
Copy Code code as follows:
FE is visible in variable objects
Testnfe ();
(function Testnfe () {
Alert (' Testnfe ');
});
FE is also visible after the definition ends
Just like a function declaration.
Testnfe ();
As we have seen, it completely violates the rules.
Second, when you assign named Fe to a variable in a declaration, JScript creates two different function objects. It is hard to name this behavior logically (especially when the name of the NFE outside it should not be accessed at all).
Copy Code code as follows:
var foo = function bar () {
Alert (' foo ');
};
Alert (typeof bar); "Function",
The interesting thing is
Alert (foo = = bar); false!
foo.x = 10;
alert (bar.x); Not defined
But the results were the same when executed.
Foo (); "Foo"
Bar (); "Foo"
See again, already disorderly become a piece.
However, it is worth noting that if you separate the variable assignment, describe NFE (such as through the group operator), and then assign it to a variable and check its equality, and the result is true, as if it were an object.
Copy Code code as follows:
(function Bar () {});
var foo = bar;
Alert (foo = = bar); True
foo.x = 10;
alert (bar.x); 10
It can be explained at this time. In fact, create two more objects again, but that actually remains one. If we think once again that the NFE here is treated as FD, then the FD bar is created in the context phase. Thereafter, the second object--function expression (FE) bar--is created in the code execution phase, and it is not stored. Accordingly, without any reference to FE bar, it is removed. So there is only one object--fd bar, and the reference to it is assigned to variable foo.
Third, by Arguments.callee indirectly referencing a function, it refers to the name of the object being activated (specifically--there are two function objects here.)
Copy Code code as follows:
var foo = function bar () {
Alert ([
Arguments.callee = = Foo,
Arguments.callee = = Bar
]);
};
Foo (); [True, False]
Bar (); [False, True]
Four, JScript treats nfe like ordinary fd, and he does not follow conditional expression rules. That is, just as a FD,NFE is created when it enters the context, the final definition in the code is used.
Copy Code code as follows:
var foo = function bar () {
Alert (1);
};
if (false) {
foo = function bar () {
Alert (2);
};
}
Bar (); 2
Foo (); 1
This behavior can also be explained in terms of "logic". In the context phase, the last encountered FD bar is created, that is, a function that contains alert (2). Thereafter, in the code execution phase, the new function--fe Bar is created and the reference to it is assigned to variable foo. So Foo activation generates alert (1). The logic is clear, but given the bug in IE, which is obviously corrupted and relies on JScript bugs, I enclose the word "logically (logically)" with quotes.
A fifth bug in JScript is related to the creation of a global object's property, which is generated by an assignment to an unqualified identifier (that is, no var keyword). Since NFE is treated as FD, accordingly, it is stored in a variable object, assigned to an unqualified identifier (that is, a generic attribute that is not assigned to a variable but a global object), so that the property is not global if the name of the function is the same as an unqualified identifier.
Copy Code code as follows:
(function () {
Without Var, it's not a variable in the current context.
It is a property of the global object
foo = function foo () {};
})();
But, outside the anonymous function, the name Foo is not available.
Alert (typeof foo); Not defined
"Logic" is already clear: in the context phase, the function declaration Foo obtains the active object of the local context of the anonymous function. In the code execution phase, name Foo already exists in AO, that is, it is used as a local variable. Accordingly, in an assignment operation, it is simply to update the property foo that already exists in AO, instead of creating a new property of the global object according to Ecma-262-3 's logic.
Functions created by the function builder
Since this function object also has its own characteristics, we distinguish it from FD and FE. The main feature is that the [[Scope]] property of this function contains only global objects:
Copy Code code as follows:
var x = 10;
function foo () {
var x = 20;
var y = 30;
var bar = new Function (' Alert (x); alert (y); ');
Bar (); "Y" is undefined
}
We see that the [[Scope]] property of function bar does not contain the ao--variable "y" of the Foo context, and the variable "x" is obtained from the global object. By the way, a function constructor can use either the New keyword or none, so these variants are equivalent.
Other features of these functions are related to equated grammar productions and joined objects. The specification provides these mechanisms as optimization recommendations (however, optimizations are not used on implementations). For example, if we have an array of 100 elements, in a loop of functions, execution may use the joined Objects mechanism. The result is that all elements in the array can be used only by one function object.
Copy Code code as follows:
var a = [];
for (var k = 0; k < k++) {
A[k] = function () {}; may have used joined objects
}
However, functions created through the function constructor are not connected.
Copy Code code as follows:
var a = [];
for (var k = 0; k < k++) {
A[k] = Function ('); It's been 100 different functions.
}
Another example associated with a Union object (joined objects):
Copy Code code 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 objects X and Object Y (using the same object) because functions (including their internal [[Scope]] properties) are fundamentally indistinguishable. Therefore, functions created through the function constructor always require more memory resources.
algorithms for creating functions
The following pseudo code describes the algorithm created by the function (except for the steps associated with the Federated object). These descriptions help you to understand more details of the function objects in ECMAScript. This algorithm is suitable for all function types.
Copy code code as follows:
F = new NativeObject ();
property [[Class]] is a "Function"
F.[[class]] = "Function"
The prototype of a function object is the prototype
F.[[prototype]] = Function.prototype
Medical use of the function itself
Activate when calling expression F [[call]]
and create a new execution context
F.[[call]] = <reference to function>
Compiling in the normal constructor of an object
[[construct]] activated with the new keyword
and allocate memory to new objects
And then call F. [[Call]] initializes the newly created object that is passed as this
F.[[construct]] = Internalconstructor
Scope chain of the current execution context
For example, to create the context of F
F.[[scope]] = Activecontext.scope
If the function passes through the new function (...) To create,
So
F.[[scope]] = Globalcontext.scope
Number of incoming arguments
F.length = Countparameters
The prototype of the F object creation
__objectprototype = new Object ();
__objectprototype.constructor = F//{Dontenum}, cannot enumerate X in loop
F.prototype = __objectprototype
Return F
Note that F.[[prototype] is a prototype of a function (constructor), F.prototype is the prototype of an object created through this function (because the terminology is often confusing and some articles f.prototype referred to as "the stereotype of the builder", which is not true).
Conclusions
This article is a little long. However, as we continue to discuss functions in the next section on objects and prototypes, I would be happy to answer any of your questions in the comments.
Other Reference
- 13.-function Definition;
- 15.3-function Objects.