- 13 Function Definition
-
Syntax
- FunctionDeclaration :
- function Identifier ( FormalParameterListopt ){
FunctionBody }
- FunctionExpression :
- function Identifieropt ( FormalParameterListopt
){ FunctionBody }
- FormalParameterList :
- Identifier
FormalParameterList , Identifier
- FunctionBody :
- SourceElements
Semantics
The production FunctionDeclaration : function Identifier (
FormalParameterListopt ){ FunctionBody } is processed for function
declarations as follows:
1. Create a new Function object as specified in 13.2 with parameters specified by
FormalParameterListopt, and body specified by FunctionBody. Pass in the scope chain of the running
execution context as the Scope.
2. Create a property of the current variable object (as specified in 10.1.3) with name
Identifier and value Result(1).
The production FunctionExpression : function ( FormalParameterListopt
){ FunctionBody } is evaluated as follows:
1. Create a new Function object as specified in 13.2 with parameters specified by
FormalParameterListopt and body specified by FunctionBody. Pass in the scope chain of the running
execution context as the Scope.
2. Return Result(1).
The production FunctionExpression : function Identifier (
FormalParameterListopt ){ FunctionBody } is evaluated as follows:
1. Create a new object as if by the expression new Object().
2. Add Result(1) to the front of the scope chain.
3. Create a new Function object as specified in 13.2 with parameters specified by
FormalParameterListopt and body specified by FunctionBody. Pass in the scope chain of the running
execution context as the Scope.
4. Create a property in the object Result(1). The property's name is Identifier, value is Result(3), and
attributes are { DontDelete, ReadOnly }.
5. Remove Result(1) from the front of the scope chain.
6. Return Result(3).
NOTE
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the
function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression
cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
The production FunctionBody : SourceElements is evaluated as follows:
1. Process SourceElements for function declarations.
2. Evaluate SourceElements.
3. Return Result(2).
- 13.1 Definitions
-
A couple of definitions are needed to describe the process of creating function objects:
- 13.1.1 Equated Grammar Productions
-
Two uses of the FunctionBody grammar production are defined to be equated when one of the following is
true:
- Both uses obtained their FunctionBody from the same location in the source text of the same ECMAScript
program. This source text consists of global code and any contained function codes according to the definitions in
10.1.2.
- Both uses obtained their FunctionBody from the same location in the source text of the same call to
eval (15.1.2.1). This source text consists of eval code and any contained
function codes according to the definitions in 10.1.2
NOTE
Two uses of FunctionBody obtained from a call to the Function constructor 15.3.1 and
15.3.2) are never equated. Also, two uses of FunctionBody obtained from two different calls to
eval are never equated, even if those two calls to eval were passed the same argument.
- 13.1.2 Joined Objects
-
When two or more Function objects are joined, they have the following special behaviours:
- Any time a non-internal property of an object O is created or set, the corresponding property is
immediately also created or set with the same value and attributes in all objects joined with O.
- Any time a non-internal property of an object O is deleted, the corresponding property is immediately
also deleted in all objects joined with O.
- If objects O and P are joined, they compare as == and === to each
other.
- Joining is transitive and symmetric, so that if objects O and P are joined and objects P
and Q are joined, then objects O and Q are also automatically joined.
NOTE
Two or more objects joined to each other are effectively indistinguishable except that they may have different internal
properties. The only such internal property that may differ in this specification is [[Scope]].
Joined objects are used as a tool for precise specification technique in this standard. They are not meant to be
used as a guideline to how Function objects are implemented in practice. Rather, in practice an implementation may
detect when the differences in the [[Scope]] properties of two or more joined Function objects
are not externally observable and in those cases reuse the same Function object rather than making a set of joined
Function objects. This is a legal optimisation because this standard only specifies observable behaviour of ECMAScript
programs.
- 13.2 Creating Function Objects
-
Given an optional parameter list specified by FormalParameterList, a body specified by FunctionBody,
and a scope chain specified by Scope, a Function object is constructed as follows:
1. If there already exists an object E that was created by an earlier call to this section's algorithm, and if
that call to this section's algorithm was given a FunctionBody that is equated to the FunctionBody given
now, then go to step 13. (If there is more than one object E satisfying these criteria, choose one at the
implementation's discretion.)
2. Create a new native ECMAScript object and let F be that object.
3. Set the [[Class]] property of F to "Function".
4. Set the [[Prototype]] property of F to the original Function prototype object as
specified in 15.3.3.1.
5. Set the [[Call]] property of F as described in 13.2.1.
6. Set the [[Construct]] property of F as described in 13.2.2.
7. Set the [[Scope]] property of F to a new scope chain (10.1.4)
that contains the same objects as Scope.
8. Set the length property of F to the number of formal properties specified in
FormalParameterList. If no parameters are specified, set the length property of F to 0. This
property is given attributes as specified in 15.3.5.1.
9. Create a new object as would be constructed by the expression new Object().
10. Set the constructor property of Result(9) to F. This property is given attributes {
DontEnum }.
11. Set the prototype property of F to Result(9). This property is given attributes as
specified in 15.3.5.2.
12. Return F.
13. At the implementation's discretion, go to either step 2 or step 14.
14. Create a new native ECMAScript object joined to E and let F be that object. Copy all non-internal
properties and their attributes from E to F so that all non-internal properties are identical in E
and F.
15. Set the [[Class]] property of F to "Function".
16. Set the [[Prototype]] property of F to the original Function prototype object
as specified in 15.3.3.1.
17. Set the [[Call]] property of F as described in 13.2.1.
18. Set the [[Construct]] property of F as described in 13.2.2.
19. Set the [[Scope]] property of F to a new scope chain (10.1.4)
that contains the same objects as Scope.
20. Return F.
NOTE
A prototype property is automatically created for every function, to allow for the possibility that the
function will be used as a constructor.
Step 1 allows an implementation to optimise the common case of a function A that has a nested function B where B
is not dependent on A. In this case the implementation is allowed to reuse the same object for B instead of creating a
new one every time A is called. Step 13 makes this optimisation optional; an implementation that chooses not to
implement it will go to step 2.
For example, in the code
function A() {
function B(x) {return x* x;}
return B;
}
function C() {
return eval("(function(x) {return x* x;})");
}
var b1 = A();
var b2 = A();
function b3(x) {return x* x;}
function b4(x) {return x* x;}
var b5 = C();
var b6 = C();
an implementation is allowed, but not required, to join b1 and b2. In fact, it may
make b1 and b2 the same object because there is no way to detect the difference between
their [[Scope]] properties. On the other hand, an implementation must not join b3
and b4 because their source codes are not equated (13.1.1). Also, an
implementation must not join b5 and b6 because they were produced by two different calls
to eval and therefore their source codes are not equated.
In practice it's likely to be productive to join two Function objects only in the cases where an implementation
can prove that the differences between their [[Scope]] properties are not observable, so one
object can be reused. By following this policy, an implementation will only encounter the vacuous case of an object
being joined with itself.
- 13.2.1 [[Call]]
-
When the [[Call]] property for a Function object F is called, the following steps are
taken:
1. Establish a new execution context using F's FormalParameterList, the passed arguments list, and the this
value as described in 10.2.3.
2. Evaluate F's FunctionBody.
3. Exit the execution context established in step 1, restoring the previous execution context.
4. If Result(2). type is throw then throw Result(2). value.
5. If Result(2). type is return then return Result(2). value.
6. (Result(2). type must be normal.) Return undefined.
- 13.2.2 [[Construct]]
-
When the [[Construct]] property for a Function object F is called, the following steps
are taken:
1. Create a new native ECMAScript object.
2. Set the [[Class]] property of Result(1) to "Object".
3. Get the value of the prototype property of F.
4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3).
5. If Result(3) is not an object, set the [[Prototype]] property of Result(1) to the
original Object prototype object as described in 15.2.3.1.
6. Invoke the [[Call]] property of F, providing Result(1) as the this value and
providing the argument list passed into [[Construct]] as the argument values.
7. If Type(Result(6)) is Object then return Result(6).
8. Return Result(1).