February 1999 Draft
JavaScript 2.0
Functions
previousupnext

Thursday, February 18, 1999

Function Definitions

To define a function we use the following syntax:

FunctionDefinition 
   [Visibility] [getter | setterfunction Identifier ( Parameters ) [TypeExpressionBlock
|  TraditionalFunctionDefinition

If Visibility is absent, the above declaration defines a local function within the current Block scope. If Visibility is present, the above declaration declares either a global function (if outside a ClassDefinition's Block) or a class function (if inside a ClassDefinition's Block) according to the declaration scope rules.

The function's result type is TypeExpression, which defaults to type any if not given. If the function does not return a value, it's good practice to set TypeExpression to void to document this fact.

Block contains the function body and is evaluated only when the function is called.

A function definition can also be traditional, which is more similar to the behavior of JavaScript 1.x function definitions.

Parameters

Parameters has one of the following forms:

Parameters 
   RequiredParameter , ... , RequiredParameter [, OptionalParameter ... , OptionalParameter] [, ... [Identifier]]
|  ... [Identifier]

If the ... is present, the function accepts more arguments than just the listed parameters. If an Identifier is given after the ..., then that Identifier is bound to an array of arguments given after the listed parameters. That Identifier is declared locally as though by the declaration const any[] Identifier.

Individual parameters have the forms:

RequiredParameter 
   [TypeExpressionIdentifier
OptionalParameter 
   [TypeExpressionIdentifier = [AssignmentExpression]

TypeExpression gives the parameter's type and defaults to type any. If the parameter name Identifier is followed by a =, then that parameter is optional. If the nth parameter is optional and a call to this function provides fewer than n arguments, then the nth parameter is set to the value of its AssignmentExpression (or undefined if the AssignmentExpression is missing), coerced to the nth parameter's type if necessary. The nth parameter's AssignmentExpression is evaluated only if fewer than n arguments are given in a call.

A RequiredParameter may not follow an OptionalParameter. If a function has n RequiredParameters and m OptionalParameters and no ... in its parameter list, then any call of that function must supply at least n arguments and at most n+m arguments. If this function has a ... in its parameter list, then any call of that function must supply at least n arguments. These restrictions do not apply to traditional functions.

The parameters' Identifiers are local variables with types given by the corresponding TypeExpressions inside the function's Block. Code in the Block may read and write these variables. Arguments are passed by value, so writes to these variables do not affect the passed arguments' values in the caller.

In addition to local variables generated by the parameters' Identifiers, each function also has a predefined arguments local variable which holds an array (of type const any[]) of all arguments passed to this function.

Evaluation Order

When a function is called, the following list indicates the order of evaluation of the various expressions in a FunctionDefinition. These steps are taken only after all of the arguments have been evaluated.

  1. Evaluate the first parameter's TypeExpression to obtain a type t.
  2. If the first parameter is optional and no argument has been supplied, evaluate the first parameter's AssignmentExpression and let it be the first parameter's value.
  3. Coerce the argument (or default) value to type t and bind the parameter's Identifier to the result.
  4. Repeat steps 1-3 for each additional parameter.
  5. If there are leftover parameters, make an array of them and the Identifier, if any, following a ... to the result.
  6. Evaluate the FunctionDefinition's result TypeExpression to obtain a result type r.
  7. Evaluate the body.
  8. Coerce the result to type r and return it.

Note that later TypeExpressions and AssignmentExpressions can refer to previously bound arguments. Thus, the following is legal:

function choice(boolean a, type b, b c, b d=) b {
  return a ? c : d;
}

The call choice(true,int,8,4) would return 8, while choice(false,int,6) would return 0 (undefined coerced to type int).

Relationship to Methods and Classes

Unless the function is a traditional function, the function definition using the above syntax does not define a class; the function's name cannot be used in a new expression, and the function does not have a this parameter. Any attempt to use this inside the function's body is an error. To define a method that can access this, use the method keyword.

If a FunctionDefinition is located at a class scope (either because it is located the top level of a ClassDefinition's Block or it has a Visibility prefix and is located inside a ClassDefinition's Block), then the function is a static method of the class. Unlike C++ or Java, JavaScript 2.0 does not use the static keyword to indicate such functions; instead, instance methods (i.e. non-static methods) are defined using the method keyword.

Getters and Setters

If a FunctionDefinition contains the keyword getter or setter, then the defined function is a getter or a setter.

A getter must not take any parameters and cannot have a ... in its Parameters list. Unlike an ordinary function, a getter is invoked by merely mentioning its name without an Arguments list in any expression except as the destination of an assignment. For example, the following code returns the string <2,3,1>:

var integer x = 0;
getter function serialNumber() integer {return ++x}

var y = serialNumber;
return "<" + serialNumber + "," + serialNumber + "," + y + ">";

A setter must take exactly one required parameter and cannot have a ... in its Parameters list. Unlike an ordinary function, a setter is invoked by merely mentioning its name (without an Arguments list) on the left side of an assignment or as the target of a mutator such as ++ or --. The result of the setter becomes the result of the assignment. For example, the following code returns the string <1,2,43>:

var integer x = 0;
getter function serialNumber() integer {return ++x}
setter function serialNumber(integer n) integer {return x=n}

var s = "<" + serialNumber + "," + serialNumber;
serialNumber = 42;
return s + "," + serialNumber + ">";

A setter can have the same name as a getter in the same lexical scope. A getter or setter cannot be extracted from its variable, so the notion of the type of a getter or setter is vacuous; a getter or setter can only be called.

Contrast the following:

var integer x = 0;
function f() integer {return ++x}
function g() funct {return f}
getter function h() funct {return f}

f;     // Evaluates to function f
g;     // Evaluates to function g
h;     // Evaluates to function f (not h)
f();   // Evaluates to 1
g();   // Evaluates to function f
h();   // Evaluates to 2
g()(); // Evaluates to 3

We can use a getter and a setter to create an alias to another variable, as in:

getter function myAlias() {return Pkg::var}
setter function myAlias(x) {return Pkg::var = x}

myAlias = myAlias+4;

Traditional Functions

Traditional function definitions are provided for compatibility with JavaScript 1.x. The syntax is as follows:

TraditionalFunctionDefinition 
   [Visibilitytraditional function Identifier ( Identifier , ... , Identifier ) Block

A function declared with the traditional keyword cannot have any argument or result type declarations, optional arguments, or getter or setter keyword. Such a function is treated as though every argument were optional and more arguments than just the listed ones were allowed. Thus, the definition

traditional function Identifier ( Identifier , ... , Identifier ) Block

behaves like the following function definition:

function Identifier ( Identifier = , ... , Identifier = , ... ) Block

Furthermore, a traditional function defines its own class and treats this in the same manner as JavaScript 1.x.

Functions in Expressions

Every function (except a getter or a setter) is also a value and has type funct. Like other values, it can be stored in a variable, passed as an argument, and returned as a result. The identifiers in a function are all lexically scoped.

Anonymous Functions

We can use a variant of a function definition to define a function inside an expression. The syntax is:

FunctionExpression 
   function [Identifier( Parameters ) [TypeExpressionBlock

This expression defines an anonymous function and returns it as a value of type funct. The anonymous function can be named by providing the Identifier, but this name is only useful to identify the function in a debugger.

To avoid confusion between a FunctionDefinition and a FunctionExpression, a Statement (and a few other grammar nonterminals) may not begin with a FunctionExpression. To place a FunctionExpression at the beginning of a Statement, enclose it in parentheses.

A FunctionDefinition is merely convenient syntax for a const variable definition and a FunctionExpression:

[Visibilityfunction Identifier ( Parameters ) [TypeExpressionBlock

is equivalent to:

[Visibilityconst funct Identifier = function Identifier ( Parameters ) [TypeExpressionBlock ;

Function Calls

Unless a function is a getter or a setter, we call that function by listing its arguments in parentheses after the function expression, just as in JavaScript 1.x:

FullPostfixExpression 
   FullPostfixExpression ( AssignmentExpression , ... , AssignmentExpression )
|  other postfix expressions

Waldemar Horwat
Last modified Thursday, February 18, 1999
previousupnext