In JavaScript, functions are objects, and they can be manipulated by programs. Javascript can assign functions to variables and pass them to other functions, for example. Since functions are objects, you can set properties on them, and even invoke methods on them.
JavaScript function definitions can be nested within other functions, and they have access to any variables that are in scope where they are defined. This means that JavaScrip functions are closures, and it enables important and powerful programming techniques.
Defining Functions
Functions are defined with the function
keyword, which can be used in. A function definition expression, or in a function declaration statement. In either form, function definitions begin with the keyword function
followed by these components:
- An identifier that names the function. The name is a required part of function declaration statements:
- A pair of parentheses around a comma-separated list of zero or more identifiers.
- A pair of curly braces with zero or more Javascript statements inside. These statements are the body of the function: they are executed whenever the function is invoked.
Notice that a function defined as an expression is only useful if it is part of a larger expression, such as an assignment or invocation, that does something with the newly defined function.
// Example 8-1. Defining JavaScript functions
// print the name and value of each property of o. Return undefiend.
function printprops(o) {
for (var p in o)
console.log(p + ": " + o[p] + "\n")
}
// Compute the distance between Cartesian points (x1, y1) and (x2, y2)
function distance(x1, y1, x2, y2) {
var dx = x2 - x1
var dy = y2 - y1
return Math.sqrt(dx * dx + dy * dy)
}
// A recursive funciton (one that clals itself) that computes factorials
// Recall that x! is the product of x and all positive integers less than it.
function factorial(x) {
if (x <= 1) return 1
return x * factorial(x - 1)
}
// This function expression defines a fucntion that squares its argument.
// Note that we assign it to a variable
var square = function(x) { return x * x }
// Function expressions can include names, which is useful for recursion.
var f = function fact(x) { if (x <= 1) return 1; else return x * fact(x - 1) }
// Function expressions can also be used as arguments to other functions:
data.sort(function(a,b) { return a - b })
// Function expressions are sometimes defined and immediately invoked:
var tensquared = (function(x) { return x * x}(10))
Note that
- the function name is optional for functions defined as expressions.
- A function declaration statement actually declares a variable and assigns a function object to it.
- A function definition expression, on the other hand, does not declare a variable.
- A name is allowed for functions, like the factorial function above, that need to refer to themselves.
- If a function definition expression includes a name, the local function scope for that function will include a binding of that name to the function object.
- In effect, the function name becomes a local variable within the function. Most functions defined as expressions do not need names, which makes their definition more compact.
- Function definition expressions are particularly well suited for functions that are used only once, as in the last two examples above.
Function Names
Any legal Javascript identifier can be a function name. Try to choose functions names that are descriptive but concise. Striking the right balance is an art that comes with experience. Well-chosen function names can make a big difference in the readability (and thus maintainability) of your code.
Function names are often verbs or phrases that begin with verbs. It is a common convention to begin function names with a lowercase letter. When a name includes multiple words, one convention is to separate words with underscores like_this()
another convention is to begin all words after the first with an uppercase letter likeThis()
. Functions that are supposed to be internal or hidden (and not part of a public API) are sometimes given names that begin with an underscore.
As described in $5.3.2, function declaration statements are “hoisted” to the top of the enclosing script or the enclosing function, so that functions declared in this way may be invoked from code that appears before they are defined.
This is not true for functions defined as expressions, however: in order to invoke a function, you must be able to refer to it, and you can’t refer to a function defined as an expression until it is assigned to a variable. Variable declarations are hoisted, but assignments to those variables are not hoisted, so functions defined with expressions cannot be invoked before they are defined.
Notice that most, but not all, of the functions in Example 8-1 contain a return
statement. The return
statement causes the function to stop executing and to return the value of its expression (if any) to the caller. If the return
statement does not have an associated expression, it returns the undefined
value. If a function does not contain a return
statement, it simply executes each statement in the function body and returns the undefined
value to the caller.
Most of the functions in Example 8-1 are designed to compute a value, and they use return
that value to their caller. The printprops()
function is different: its job is to output the names and values of an object’s properties. The value of an invocation of the printprops()
function is always undefined
. (Functions with no return value are sometimes called procedures.)
8.1.1 Nested Functions
In JavaScript, functions may be nested within other functions. For example:
function hypotenuse(a, b) {
function square(x) { return x * x }
return Math.sqrt(square(a) + square(b))
}
The interesting thing about nested functions is their variable scoping rules: they can access the parameters and variables of the function (or functions) they are nested within. In the code above, for example ,the inner function square()
can read and write the parameters a
and b
defined by the outer function hypotenuse()
. These scope rules for nested functions are very important, and we’ll consider them again in 8.6.
As noted in 5.3.2, function declaration statements are not true statements, and the ECMAScript specification only allows them as top-level statements. They can appear in global code, or within other functions, but they cannot appear inside of loops, conditionals, or try/catch/finally
or with
statements. Note that this restriction applies only to functions declared as statements. Functions definition expressions may appear anywhere in your JavaScript code.
Invoking Functions
The JavaScript code that makes up the body of a function is not executed when the function is defined but when it is invoked. JavaScript functions can be invoked in four ways:
- as functions,
- as methods,
- as constructors, and
- Indirectly through their
call()
andapply()
methods.
Function Invocation
Functions are invoked as functions or as methods with an invocation expression.
- An invocation expression consists of a function expression that evaluates to a function an object followed by an open parenthesis, a comma-separated list of zero or more argument expressions, and a close parenthesis.
- If the function expression is a property-access expression – if the function is the property of an object or an element of an array – then it is a method invocation expression. That case will be explained below. The following code includes a number of regular function invocation expressions:
printprops({x:1})
var total = distance(0,0,2,1) + distance(2,1,3,5)
var probability = factorial(5)/factorial(13)
In an invocation, each argument expression (the ones between the parentheses) is evaluated, and the resulting values become the arguments to the function. These values are assigned to the parameters named in the function definition, a reference to a parameter evaluates to the corresponding argument value.
For regular function invocation, the return value of the function becomes the value of the invocation expression. If the function returns because the interpreter reaches the end, the return value is undefined
. If the function returns because the interpreter executes a return
, the return value is the value of the expression that follows the return
or undefined
if the return
statement has no value.
For function invocation in ECMAS3 and nonstrict ECMAScript 5, the invocation context (the this
value) is the global object. In strict mode, however, the invocation context is undefined
.
Functions written to be invoked as functions do not typically use the this
keyword at all. It can be used, however, to determine whether strict mode is in effect:
// Define and invoke a function to determine if we're in strict mode.
var strict = (function() { return !this }())
Method invocation
A method is nothing more than a JavaScript function that is stored in a property of an object. If you have a function f
and an object o
, you can define a method named m
of o
with the following line:
o.m = f
Having defined the method m()
of the object o
, invoke it like this:
o.m()
Or, if m()
expects two arguments, you might invoke it like this:
o.m(x, y)
The code above is an invocation expression: it includes a function expression o.m
and two argument expressions, x
and y
. The function expression is itself a property access expressions, and this means that the function is invoked as a method rather than a regular function.
- The arguments and return value of a method invocation are handled exactly as described above for regular function invocation.
- Method invocations differ from function invocations in one important way, however: the invocation context.
Property access expressions consist of two parts:
- an object (In this case
o
) and a property name (m
). - In a method invocation expression like this, the object
o
becomes the invocation context, and the function body can refer to that object by using the keywordthis
. Here is a concrete example:
var calculator = { // An object literal
operand1: 1,
operand2: 1,
add: function() {
// Note the use of this keyword to refer to this object.
this.result = this.operand1 + this.operand2
}
}
calculator.add() // A method invocation to compute 1+1.
calculator.result // => 2
Most method invocations use the dot notation for property access, but property access expressions that use square brackets also cause method invocation. The following are both method invocations, for example:
o["m"](x,y) // Another way to write o.m(x,y)
a[0](z) // Also a method invocation (assuming a[0] is a function).
Method invocations may also invoke more complex property access expression:
customer.surname.toUpperCase() // Invoke method on customer.surname
f().m() // Invoke method m() on return value of f()
Methods and the this
keyword are central to the object-oriented programming paradigm. Any function that is used as a method is effectively passed an implicit argument — the object through which it is invoked.
- Typically, a method performs some sort of operation on that object, and the method-invocation syntax is an elegant way to express the fact that a function is operating on an object. Compare the following two lines:
rect.setSize(width, height)
setRectSize(rect, width, height)
The hypothetical functions invoked in these two lines of code may perform exactly the same operation on the (hypothetical) object rect
, but the method-invocation syntax is the first line more clearly indicates the idea that it is the object rect
that is the primary.
Method Chaining
When methods return objects, you can use the return value of one method invocation as part of a subsequent invocation. This results in a series (or “chain” or “cascade”) of method invocations as a single expression. When working with the jQuery
library, for example ,ti si common to write code lie this:
// Find all headers, map to their ids, convert ot an array an sort them
$(":header").map(function() {return this.id }).get().sort()
When you write a method that does not have a return value of tis own, consider having the method return this
. If you do this consistently throughout your API, you will enable a style of programming known as method chaining in which an object can be named once and then multiple methods can be invoked on it:
shape.setX(100).setY(100).setSize(50).setOutline("red").setFill("blue").draw()
Don’t confuse method chaining with constructor chaining, which is described in 9.7.2.
Note that this
is a keyword, not a variable or property name. JavaScript syntax does not allow you to assign a value to this
.
Unlike variables the this
keyword does not have a scope, and nested functions do not inherit the this
value of their caller. If a nested function is invoked as a method, its this
value is the object it was invoked on. If a nested function is invoked as a function then its this
value will be either the global object (non-strict mode) or undefined
(strict mode).
- If you want to access the
this
value of the outer function, you need to store that value into a variable that is in scope for the inner function. It is common to use the variableself
for this purpose. For example:
var o = { // An object `o`
m: function() { // Method `m` of the object.
var self = this // Save the this value in a variable.
console.log(this === o) // Prints "true": this is the object `o`.
f() // Now call teh helper function `f()`.
function f() { // A nested function `f`
console.log(this === o) // "false": this global or undefined
console.log(self === o) // "true": self is the outer this value.
}
}
}
o.m() // Invoke the method `m` on the object `o`.
8.2.3 Constructor Invocation
If a function or method invocation is preceded by the keyword new
, then it is a constructor invocation. Constructor invocations differ from regular function and method invocations in their handling of arguments, invocation context, and return value.
If a constructor invocation includes an argument list in parentheses, those argument expressions are evaluated and passed to the function in the same way they would be for function and method invocations. But if a constructor has no constructor invocation syntax allows the argument list and parentheses to be omitted entirely. You can always omit a pari invocation and the following tw aliens, for example, are equivalent:
var o = new Object()
var o = new Object
A constructor invocation creates a new, empty object that inherits from the prototype
property of the constructor. Constructor functions are intended to initialize objects and this newly created object is used as the invocation context, so the constructor function can refer to this with the this
keyword.
- Note that the new object is used as the invocation context even if the constructor invocation looks like a method invocation. That is, in the expression
new o.m()
,o
is not used as the invocation context.
Constructor functions do not normally use the return
keyword. They typically initialize the new object and then return implicitly when they reach the end of their body. In this case, the new object is the value of the constructor invocation expression.
- If, however, a constructor explicitly used the
return
statement to return an object, then that object becomes the value of the invocation expression. - If the constructor uses
return
with no value, or if it returns a primitive value, that return value is ignored and the new object is used a the value of the invocation.
8.2.4 Indirect Invocation
JavaScript functions are objects and like all JavaScript objects, they have methods. Two of these methods, call()
and apply()
, invoke the function indirectly. Both methods allow you to explicitly specify the this
value for the invocation, which means you can invoke any function as a method of any object, even if it is not actually a method of that object.
The
call()
andapply()
methods are described in detail in 8.7.3.
Function Arguments and Parameters
JavaScript function definitions do not specify an expected type for the function parameters, and function invocations do not do any type checking on the argument values you pass. In fact, JavaScript function invocations do not even check the number of the arguments being passed. The subsections that follow describe what happens when a function noise invoked with fewer arguments than declared parameters or with more arguments than declared parameters. They also demonstrate how you can explicitly test the type of function arguments if you need to ensure that a function is not invoked with inappropriate arguments.
Optional Parameters
When a function is invoked with fewer arguments than declared parameters, the additional parameters are set to the undefined
value. It is often useful to write functions so that some arguments are optional and may be omitted when the function is invoked. To do this, you must be able to assign a reasonable default value to parameters that are omitted. Here is an example:
// Append the names of the enumerable properties of object `o` to the array `a`, and return `a`. If `a` is omitted, create and return a new array.
function getPropertyNames(o, /* optional */ a) {
if (a === undefined) a = [] // If undefined, use a new array
for (var property in o) a.push(property)
return a
}
// This function can be invoked with 1 or 2 arguments:
var a = getPropertyNames(o) // Get o's properties into a new array
getProeprtyNames(p, a) // append p's properties to that array
Instead of using an If
statement in the first line of this function, you can use the ||
operator in this idiomatic way:
a = a || []
Note that when designing functions with optional arguments, you should be sure to put the optimal ones at the end of the argument list so that they can be omitted. The programmer who calls your function cannot omit the first argument and pass the second: she would have to explicitly pass undefined
the first argument. Also note the use of the comment /* optional */
in the function definition to emphasize the fact that the parameter is optional.
Variable-Length Argument Lists: The Arguments Object
When a function is invoked with more argument values than there are parameter names, there is no way to directly refer to the unnamed values. The Arguments object provides a solution to this problem. Within the body of a function, the identifier arguments
refers to the Arguments object for that invocation. The Arguments object is an array-like object that allows the argument values passed to the function to be retrieved by number, rather than by name.
Suppose you define a function f
that expects to be passed argument, x
. If you invoke this function with two arguments, the first argument is accessible within the function by the prater name x
or as arguments[0]
. The second argument is accessible only as arguments[[1]
. Furthermore, like true arrays, arguments
has a length
property that specifies the number of elements it contains. Thus, within the body of the function f
, invoked with two arguments, arguments.length
has the value 2
.
The argument object is useful in a number of ways. The following example shows how you can use it to verify that a function is invoked withe the expected number of arguments, since JavaScrip doesn’t do this for you:
function f(x, y, z)
{
// First, verify that the right number of arguments was passed
if (arguments.length != 3) {
throw new Error("function f called with " + arguments.length + "arguments, but it expects 3 arguments.")
}
// Now do the actual function ...
}
Note that it is often unnecessary to check the number of arguments like this. JavaScript’s default behavior is fine in most cases: missing arguments are undefined
and extra arguments are simply ignored.
One important use of the Arguments object is to write functions that operate on any number of arguments. The following function accepts any number of numeric arguments and returns the value of the largest argument it is passed (see also the built-in function Math.max()
, which behaves the same way):
funciton max(/* ... */) {
var max = Number.NEGATIVE_INFINITY
// Loop through the arguments, looking for, and remembering, the biggest
for (var i = 0; i < arguments.length; i++)
if (arguments[i] > max) max = arguments[i]
// Return the biggest
return max
}
var largest = max(1, 10, 100, 2, 3, 1000, 4, 5, 10000, 6) // => 10000
Functions like this one that can accept any number of arguments are called variadic functions, variable parity functions or varags functions.
Note that varargs functions need not allow invocations with zero arguments. It is perfectly reasonable to use the arguments[]
object to write functions that expect some fixed number of named and required arguments followed by.an arbitrary number of unnamed optional arguments.
Remember that arguments
is not really an array; it is an Arguments object. Each Arguments object defines numbered array elements and a length
property, but it is not technically an array; it is better to think of it as an object that happens to have some numbered properties.
The Arguments object has one very unusual feature. In non-strict mode, when a function has named parameters, the array elements of the Arguments object are aliases for the parameters that hold the function arguments. The numbered elements of the Arguments object and the parameter names are like two different names for the same variable. Changing the value of an argument with an argument name changes the value that is retrieved through the argument[]
array. Conversely, changing the value of argument through the arguments[]
array changes the value that is retrieved by the argument name. Here is an example that clarifies this:
function f(x) {
console.log(x) // Display the initial value of the argument
arguments[0] = null // Changing the array element also changes x!
console.log(x) // Now displays "null"
}
The callee and caller properties
In addition to tis array elements, the Arguments object defines callee
and caller
properties. In ECMAScript 5 strict mode, these properties are guaranteed to raise a TypeError if you try to read or write them. Outside of strict mode, however ,the ECMAScript standard says that the callee
property refers to the currently running function. caller
is a nonstandard but commonly implemented property that refers to the function that called this one. The caller
property gives access to call stack, and the callee
property is occasionally useful to allow unnamed functions to call themselves recursively:
var factorial = function(x) {
if (x <= 1) return 1
return x * arguments.callee(x-1)
}
Using Object Properties as Arguments
When a function has more than three parameters, it becomes difficult for the programmer who invokes the function to remember the correct order in which to pass arguments. To save the programmer the trouble of consulting the documentation each time she uses the function, it can b nice to allow arguments to be passed as name/value pairs in an order. To implement this style of method invocation, define your function to elect a single object as its argument and then have suers of the function pass and object that defines the required name/value pairs. The following code gives an exhale and also demonstrates that this style of function invocation allows the function to specify defaults for any arguments that are omitted:
function arraycopy(/* array */ from, /* index */ from_start,
/* array */ to, /* index */ to_start,
/* integer */ length)
{
// code goes here
}
function easycopy(args) {
arraycopy(args.from,
args.from_start || 0, // note default value provided
args.to,
args.to_start || 0,
args.length)
}
// Here is how you might invoke easycopy()
var a = [1, 2, 3, 4], b = []
easycopy({from: a, to: b, length: 4})
Argument Types
JavaScript method parameters have no declared types, and no type checking is performed on the values you pass to a function. You can help to make your code self-documenting by choosing descriptive names for function arguments and also by including argument types in comments, as in the arraycopy()
method just shown. For arguments that are optional, you can include the word “optional” in the comment. And when a method can accept any number of arguments, you can use an ellipsis:
function max(/* number ... */) { /* code here */ }
JavaScript performs liberal type conversions as needed. So fi you write a function that expects a string argument and then call that function with a value of some other type, the value you passed will simply be converted to a string when the function tries to sue it as a string. All primitive types can be converted to strings, and all objects have (if not necessarily useful ones) toString()
methods, so an error never occurs in this case.
This is not always true, however. Consider again the arrarycopy()
method shown earlier. It expects an array as its first argument. Any plausible implementation will fail if that first argument is anything but an array. Unless you are writing a “throwaway” function that will be called only once or twice, it may be worth adding code to check the types of arguments like this. It is better for a function to fill immediately and predictably when passed bad values than to begin executing and fail later with an error message that is likely to be unclear. Here is an example function that performs type-checking. Note that it uses the IsArrayLike()
function from 7.11:
// Return the sum of the elements of array (or array-like object) a.
// The elements of a must all be numbers or null and undefined are ignored.
function sum(a) {
if (isArrayLike(a)) {
var total = 0
for (var i = 0; i < a.length; i++) {
var element = a[i]
if (element == null) continue
if (isFinitie(element)) total += element
else throw new Error("sum(): elements must be finite numbers")
}
return total
}
else throw new Error("sum(): argument must be array-like")
}
This sum()
method is fairly strict about the argument it accepts and throws suitably informative errors if it is passed bad values. It does offer a bit of flexibility, however, by working with array-like object as well as true arrays and by ignoring null
and undefined
array elements.
JavaScript is a very flexible and loosely typed language, and sometimes it is appropriate to write functions that are flexible about the number and type of arguments they are passed. The following flexisum()
method takes this approach (probably to an extreme).
function flexisum(a) {
var total = 0
for (var i = 0; i < arguments.length; i++) {
var element = arguments.[i], n
if (element == null) continue // Ignore null and undefined arguments
if (isArray(element))
n = flexisum.apply(this, element) // compute tis sum recursively
else if (typeof element === "function")
n = Number(element()) // invoke and convert
else n = Number(element)
if (isNan(n)) // If we couldn't conver to a number, throw an error
throw Error("flexisum(): can't convert " + element + " to number")
total += n
}
return total
}
Functions As Values
The most important features of functions are that they can be defined and invoked. Functions definition and invocation are syntactic features of JavaScript and of most other programming languages. In JavaScript, however, functions are not only syntax but also values, which means they can be assigned to variables, stored in properties of objects or the elements of arrays, passed a arguments to functions, and so on.
To understand how functions can be JavaScript data as well as JavaScript syntax, consider this function definition:
function square(x) { return x*x }
The definition creates a new function object and assigns it to the variable square
. The name of a function is really immaterial; it is simply the name of a variable that refers to the function object. The function can be assigned to another variable and still work the same way:
var s = square
square(4)
s(4)
Functions can also be assigned to object properties rather than variables. When you do this, they’re called methods:
var o = {square: function(x) { return x*x }}
var y = o.square(16)
Functions don’t event require names at all, as when they’re assigned to array elements:
var a = [function(x) { return x*x }, 20]
a[0](a[1])
The syntax of this last example looks strange, but it is still a legal function invocation expression!
// Example 8-2. Using functions as data
// We deifine some simple functions here
function add(x, y) { return x + y }
function substract(x,y) { return x - y }
function multiply(x,y) { return x * y }
function divide(x,y) { return x / y }
// Here's a function that takes one of the above functions
function operate(operator, operand1, operand2) {
return operator(operand1, operand2)
}
// We could invoke this function like this to compute the value (2 + 3) + (4 * 5):
var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5))
// For the sake of the example, we implemnt the simple functions again,
// this time using function literals within an object literal
var opeartors = {
add: function(x,y) { return x+y }
substract: function(x,y) { return x-y }
multiply: function(x,y) { return x*y }
divide: function(x,y) { return x/y }
pow: Math.pow // Works for predefined functions too
}
// This function takes the name of an oepartor, looks up that opeartor in the object, adn then invokes it on the supplied operands. Note the syntax used to inovoke the operator function.
function operate2(operation, operand1, operand2) {
if (typeof operators[operation] === "function")
return opeartors[operation](operand1, openrand2)
else throw "unknown operator"
}
// Compute the value ("hello" + " " + "world") like this:
var j = operate2("add", "hello", operate2("add", " ", "world"))
// Using the predefined `Math.pow()` function:
var k = operate2("pow", 10, 2)
As another example of functions as values, consider the Array.sort()
method.
Defining Your Own Function Properties
Functions are not primitive values in JavaScript, but a specialized kind of object, which means that functions can have properties. When a function need a “static” variable whose value persists across invocations, it is often convenient to use a property of the function, instead of cluttering up the namespace by defining a global variable.
- For example, suppose you want to write a function that returns a unique integer whenever it is invoked. Then function must never return the same value twice. In order to manage this, the function needs to keep track of the value twice. In order to manage this, the function needs to keep track of the values it has already returned and this information must persist across function invocations.
- You could store the information in a global variable, but that is unnecessary, because the information is used only by the function itself.
- It is better to store the information in a property of the Function object.
// Initialize the counter property of the function object.
// Function declarations are hoisted so we realy can do this assignment before the function declaration.
uniqueInteger.counter = 0
// This functino returns a different integer each time it is called.
// It uses a property of itself to remember the next value to be returned.
function uniqueInteger() {
return uniqueInteger.counter++ // Increment and return counter property
}
As another example, consider the following factorial()
function that uses properties of itself (treating itself as an array) to cache previously computed results:
// Compute factorials and cache results as properties of the function itself.
function factorial(n) {
if (isFinite(n) && n > 0 && n == Math.round(n)) {
if !(n in factorial)
factorial[n] = n * factorial(n-1)
return factorial[n]
}
else return NaN
}
factorial[1] = 1 // Initialize the cache to hold this base case.
Functions as Namespaces
Recall from 3.10.1 that JavaScript has function scope: variables declared within a function are visible throughout the function (including within nested functions) but do not exist outside of the function. Variables declared outside of a function are global variables and are visible throughout your JavaScript program. JavaScript does not define any way to declare variables that are hidden within a single block of code, and for this reason, it is sometimes useful to define a function simply to act as a temporary namespace in which you can define variables without polluting the global namespace.
Suppose, for example, you have a module of JavaScript code that you want to use in a number of different Javascript programs (or, for client-side JavaScript, on a number of different web pages). Assume that this code, like most code, define variables to store the intermediate results of its computation. The problem is that since this module will be used I many different programs, you don’t know whether the variables it creates will conflict with variables used by the programs that import it. The solution, of course, is to put the code into a function and then invoke the function. This way, variables that would have been global become local to the function:
funciton mymodule() {
// Module code goes here.
// Any variables used by the module are local to this function
// instead of cluttering up the global namespace.
}
mymodule() // But don't forget to invoke the function!
This code defines only a single global variable: the function name “mymodule”. If defining even a single property is too much, you can define and invoke an anonymous function in a single expression:
(function() { // mymodule function rewritten as an unnamed expression
// Module code goes here.
}()) // end the function literal and invoke it now.
This technique of defining and invoking a function in a single expression is used frequently enough that it has become idiomatic. Note the use of parentheses in the code above. The open parenthesis before function
is required because without it, the JavaScript interpreter tries to parse the function
keyword as a function declaration statement. With the parenthesis, the interpreter correctly recognizes this as a function definition expression. It is idiomatic to use the parentheses, even when they are not required, around a function that is to be invoked immediately after being defined.
Example 8-3 demonstrates the namespace technique.
// Example 8-3 The extend() function, patched if necessary.
var extend = (function() { // Assign the return value of this function
// First check for the presence of hthe bug before patching it.
})
Closures
Like most modern programming languages, JavaScript uses lexical scoping. This means that functions are executed using the variable scope that was in effect when they were defined, not the variable scope that is in effect when they are invoked. In order to implement lexical scoping, the internal state of a JavaScript function object must include not only the code of the function but also a reference to the current scope chain. This combination of a function object and a scope (a set of variable bindings) in which the function’s variables are resolved is called.a closure in the computer science literature.
Technically, all JavaScript functions are closures: they are objects, and they have a scope chain associated with them. Most functions are invoked using the same scope chain that was in effect when the function was defined, and it doesn’t really matter that there is a closure involved.
Closures become interesting when they are invoked under a different scope chain than the one that was in effect when they were defined. There are a number of powerful programming techniques that involves this kind of nested function closures, and their use has become relatively common in JavaScript programming. Closures may seem confusing when you first encounter them, but it is important that you understand them well enough to use them comfortably.
The first step to understanding closures is to review the lexical scoping rules for nested functions. Consider the following code:
var scope = "global scope" // A global variable
function checkscope() {
var scope = "local scope" // A local variable
function f() { return scope } // Return the value in scope here
return f()
}
checkscope() // => "local scope"
The checkscope()
function declares a local variable and then defines and invokes a function that returns the value of that variable. It should be clear to you why the call to checkscope()
returns “local scope”.
var scope = "global scope"
funciton checkscope() {
var scope = "local scope"
function f() { return scope }
return f
}
checkscope()()
In this code, a pair of parentheses has moved from inside checkscope()
to outside of it. Instead of invoking the nested function and returning tis result, checkscop()
mow just return the nested function object itself. What happens when we invoke that nested function (with the second pair of parentheses in the last line of code) outside of the function in which it was defined?
Remember the fundamental rule of lexical scoping: JavaScript functions are executed using the scope chain that was in effect when they were defined. The nested function f()
was defined under a scope chain in which variable scope
was bound to the value “local scope”. The binding is still in effect when f
is executed, wherever it is executed from. So the last line of code above returns “local scope”, not “global scope”. This, in a nutshell, is the surprising and powerful nature of closures: they capture the local variable (and parameter) bindings of the outer function within which they are defined.
Implementing Closures
Closures are easy to understand if you simply accept the lexical scoping rule: functions are executed using the scope chain that was in effect when they were defined. She programmers find closures confusing, however, because they get caught up in implementation details. Surely, they think, the local variables defined in the outer function cease to exist when the outer function returns, so how can the nested function execute using a scope chain that does not exist anymore? If you’re wondering about this your self, then you have probably even exposed to low-level programming languages like C and to stack-bases CPU architectures: if a function’s local variable are defined on a CPU stack, then they would indeed cease to exist when the function returned.
But remember our definition of scope chain from 3.10.3. We described it as a list of objects, not a stack of bindings. Each time a JavaScript function is invoked, a new object is created to hold the local variables for that invocation, and that object is added to the scope chain. When the function returns, that variable bindings object is removed from the scope chain.
- If there were no nested functions, there are no more references to the binding object and it gets garbage collected.
- If there were nested functions defined, then each of those functions has a reference to the scope chain, and that scope chain refers to the variable binding object.
- If those nested function objects remained within their outer function, however, then they themselves will be garbage collected, along with the variable binding object they referred to.
- But if the function defines a nested function and returns it or stores it into a property somewhere, then there will be an external reference to the nested function. It won’t be garbage collected, and the variable binding object it refers won’t be garbage collected either.
In 8.4.1, we defined a uniqueInteger()
function that used a property of the function itself to keep track of the next value to be returned. A shortcoming of that approach is that buggy or malicious code could reset the counter or set it to a nonifnteger, causing the uniqueInteger()
function to violate the “unique” or the “Integer” part of its contract. Closures capture the local variables of a single function invocation and use those variables as private state. Here is how we could rewrite the uniqueInteger()
function using closures:
var uniqueInteger = (function() {
var counter = 0
return function() { return counter++ }
}())
In order to understand this code, you have to read it carefully.
- At first glance, the first line of code looks like it is assigning a function to the variable
uniqueInteger
. In fact, the code is defining and invoking (as hinted by the open parenthesis on the first line) a function, so it is the return value of the function that is being assigned touniqueInteger
. - Now, if we study the body of the function, we see that its return value is another function. It is the nested function object that gets assigned to
uniqueInteger
. The nested function has access to the variables in scope, and can use thecounter
variable defined in the outer function. Once that outer function returns, no other code can see thecounter
variable: the inner function has exclusive access to it.
Private variables like counter
need not be exclusive to a single closure: it is perfectly possible for tow or more nested functions to be defined within the same outer function and share the same scope chain. Consider the following code:
function counter() {
var n = 0
return {
count: function() { return n++ }
reset: function() { n = 0 }
}
}
var c = counter(), d = counter() // Create two counters
c.count() // => 0
d.count() // => 0: they count independently
c.reset() // => reset() and count() methods share state
c.count() // => 0: because we reset c
d.count() // => 1: d was not reset
The counter()
function returns a “counter” object. This object has two methods: count()
returns the next integer, and reset()
resets the internal state.
- The first thing to understand is that two methods share access to the private variable
n
. - The second thing to understand is that each invocation of
counter()
creates a new scope chain and a new private variable. So if you callcounter()
twice, you get two counter objects with different private variables.- So if you call
counter()
twice, you get two counter objects with different private variables. - Calling
count()
andreset()
on one counter object has no effect on the other.
- So if you call
It is worth noting here that you can combine this closure technique with property getters and setters. The following version of the counter()
function is a variation on code that appeared in 6.6, but it uses closures for private state rather than relying on a regular object property:
function counter(n) {
return {
// Property getter method returns and increments private couner var.
get count() { return n++ },
// Property setter doesn't allow the value of n to decrease
set count(m) {
if (m >= n) n = m
else throw Error("count can only be set to a larger value")
}
}
}
var c = counter(1000)
c.count // => 1000
c.count // => 1001
c.count = 2000
c.count
c.count = 2000 // => Error!
Note that this version of the counter()
function does not declare a local variable, but just uses its parameter n
to hold the private state shared by the property accessor methods. This allows the caller of counter()
to specify the initial value of the private variable.
Example 8.4 is a generalization onto the shared private state through closures technique we’ve been demonstrating here. This example defines an addPrivateProperty()
function that defines a private variable and two nested functions to get and set the value of that variable. It adds these nested functions as methods of object you specify:
// Example 8-4. Private property accessor methods closures
// This function adds property accessor methods for a property with the specified name to the object `o`. The methods are named get<name> and set<name>. If a predicate funciton is supplied, the setter method usees it to test its argumetn for validity before storing it.
// If the predicate returns false, the setter method throws an exception.
//
// The unusual thing about this fucntion is that the property value that is , manipulated by the getter and setter methods is not stored in the object to. Instead, the value is stored only in a local variable in this function. The getter and setter methods are also defiend locally to this funciton and therefore have access to this local variable. This means that the value is private to the access to this lcoal variable.
// This means that the value is private to the two accessor methods, and it cannot be set or modified except through the setter method.
function addPrivateProperty(o, name, predicate) {
var value // This is the property value
// The getter method simply returns the value.
o["get" + name] = function() { return value }
// The setter method stores the value or throws an exception if the predicate rejects the valeu.
o["set" + name] = function(v) {
if (predicate && !predicate(v))
throw Error("set" + name + ": invalid value " + v)
else
value = v
}
}
// The following code demonstrates teh addPrivateProperty() method.
var o = {} // Here is an empty object
// Add property accessor methods getName and setName()
// Ensure that only string values are allowed
addPrivateProperty(o, "Name", function(x) { return typeof x == "string" })
o.setName("Frank") // Set the property value
console.log(o.getName()) // Get the property value
o.setName(o) // Try to set a value of the wrong type
We’ve now seen a number of examples in which two closures are defined in the same scope chain and share access to the same private variable or variables. This is an important technique, but it is just as important to recognize when closures inadvertently share access to a variable that they should not share. Consider the following code:
// This function returns a function taht always returns v
function constfuncs(v) { return function() { return v } }
// Create an array of constant functions:
var funcs = []
for (var i = 0; i < 10; i++) funcs[i] = constfunc(i)
// The function at array element 5 returns the value 5
funcs[5]() // => 5
When working with code like this that creates multiple closures using a loop, it is a common error to try to move the loop within the function that defines the closures. Think about the following code, for example:
// Return an arary of functions that return the values 0-9
function constfuncs)() {
var funcs = []
for (var i = 0; i < 10; i++)
funcs[i] = function() { return i }
return funcs
}
var funcs = constfuncs()
funcs[5]() // What does this return?
The code above creates 10 closures, and stores them in an array. The closures are all defined within the same invocation of the function, s o they share access to the variable i
. When constfuncs()
returns, the value of the variable I
is 10
, and all 10
closures share this value. Therefore, all the functions in the returned array of functions return the same value, which is not what we wanted at all. It is important to remember that the scope chain associated with a closure is “live.” Nested functions do not make private copies of the scope or making status snapshots of the variable bindings.
Another thing to remember when writing closures is that this
is a JavaScript keyword, not a variable. As discussed earlier, every function invocation has a this
value, and a closure cannot access the this
value of its outer function unless the outer function has saved that value into a variable:
var self = this; // Save this value in a variable for use by nested funcs.
The arguments
bindings is similar. This is not a language keyword, but it is automatically declared for every function invocation. Since a closure has its own binding for arguments
, it cannot access the outer function’s arguments array unless the outer function has saved that array into a variable by a different name:
var outerArguments = arguments // Save for user by nested functions
Example 8-5, later this chapter, defines a closure that uses these techniques to refer to both the this
and arguments
values of the outer function.
Function Properties, Methods, and Constructor
We’ve seen that functions are values in JavaScript programs. The typeof
operator returns the string “function” when applied to a function, but functions are really a specialized kind of JavaScript object. Since functions are objects, they can have properties and methods, just like any other object. There is even a Function()
constructor to create new function objects. The subsections that follow document function properties and methods and the Function()
constructor. You can also read about these in reference section.
The length Property
Within the body of a function, arguments.length
specifies the number of arguments that were passed to the function. The length
property of a function itself, however, has a different meaning. This ready-only property returns the arity
of the function – the number of parameters it declares in its prameter list, which is usually the number of arguments that the function expects.
The following code defines a function named check()
that is passed the arguments
array from another function. It compares arguments.length
(the number of arguments actually passed) to arguments.callee.length
the number expected) to determine whether the function was passed the right number of arguments. If not, it throws an exception. The check()
function is followed by a test function f()
that demonstrates how check()
can be used:
// This function uses arguments.callee, so it won't work in strict mode.
function check(args) {
var actual = args.length // The actual number of arguments.
var expected = args.callee.length // The expected number of arguments.
if (actual !== expected) // Throw an expection if they differ.
throw Error("eExpected" + expected + "args; got " + actual)
}
function f(x, y, z) {
check(arguments) // Check that the actual # of args matches expected #.
return x + y + z // Now do the rest of the function normally.
}
The prototype Property
Every function has a property
that refers to an object known as the property object. Every function has a different prototype object. When a function is used as a constructor, the new created object inherits properties from the prototype object. Prototypes and the prototype
property are discussed in 6.1.3 and will be covered again in Chapter 9.
The call()
and apply()
Methods
call()
and apply()
allow you to indirectly invoke a function as if it were a method of some other object. (We used the call()
method in Example 6.4 to invoke Object.prototype.toString
on an object whose class we wanted to determine, for example.) The first argument to both call()
and apply()
is the object on which the function is to be invoked; this argument is the invocation context and becomes the value of the this
keyword within the body of the function; this argument is the invocation context and becomes the value of the this
keyword within the body of the function. To invoke the function f()
as a method of the object o
(passing no arguments), you could use either call()
or apply()
:
f.call(o)
f.apply(o)
Either of the lines of code above are similar to the following (which assume that o
does not already have a property named m
):
o.m = f // Make `f` a temporary method of o.
o.m() // Invoke it, passing no arguments.
delete o.m // Remove the temporaty method.
In ECMAScript 5 strict mode the firs argument to call()
or apply()
becomes the value of this
, even if it is a primitive value of null
or undefined
. In ECMAScript 3 and non-strict mode, a value of null
or undefined
is replaced with the global and a primitive value is replaced with the corresponding wrapper object.
Any arguments to call()
after the first invocation context argument are the values that are passed to the function that is invoked. For example, to pass two numbers to the function f()
and invoke it as if it were a method of the object o
, you could use code like this:
f.call(o, 1, 2)
The apply()
method is like the call()
method, except that the arguments to be passed to the function are specified as an array:
f.apply(o, [1,2])
If a function is defined to accept an arbitrary number of arguments, the apply()
method allows you to invoke that function on the contents of an array of arbitrary length. For example, to find the largest number in an array of numbers, you could use the apply()
method to pass the elements of the array to the Math.max()
function:
var biggest = Math.max.apply(Math, array_of_numbers)
Note that apply()
works with array-like objects as well as true arrays. In particular, you can invoke a function with the same arguments as the current function by passing the arguments
array directly to apply()
. The following code demonstrate:
// Replace the method named m of the object o with a version that logs messages before and after invoking the original method.
function trace(o, m){
var original = o[m] // Remeber original method in the closure.
o[m] = function() {
console.log(new Date(), "Entering:", m) // Remeber original method in the colsure.
var result = original.apply(this, arguments)
console.log(new Date(), "Exiting:", m) // Log message.
return result
}
}
This trace()
function is passed an object and a method name. It replaces the specified method with a new method that “wraps” additional functionality around the original method. This kind of dynamic alteration of existing methods is sometimes called “monkey-patching.”
8.7.4 The bind()
Method
The bind()
method was added in ECMAScript 5, but it is easy to simulate in ECMAScript 3. As its name implies, the primary purpose of bind()
is to bind a function to an object. When you invoke the bind()
method on a function f
and pass an object o
, the method returns a new function. Invoking the new function (as a function) invokes the original function f
as a method of o
. Any arguments you pass to the new function are passed to the original function. For example:
function f(y) { return this.x + y } // This function needs to be bound
var o = { x : 1 }
var g = f.bind(o)
g(2)
It is easy to accomplish this kind of binding with code like the following:
// Return a function that invokes f as a method of o, passing all its arguments.
function bind(f, o) {
if (f.bind) return f.bind(o)
else return function() {
return f.apply(o, arguments)
}
}
The ECMAScript 5 bind()
method does more than just bind a function to an object. It also performs partial application: any arguments you pass to bind()
after the first are bound along with the this
value. Partial application is a common technique in functional programming and is sometimes called currying
. Here are some examples of the bind()
method used for partial application:
var sum = function(x,y) { return x + y }
// Create a new functino like sum, but wit with the this value bound to null and the 1st argument bound to 1. This new function expects just on arg.
var succ = sum.bind(null, 1)
succ(2)
function f(y,z) { return this.x + y + z }
var g = f.bind({x:1}, 2)
g(3)
We can bind the this
value and performa partial application in ECMAScript 3. The standard bind()
method can be simulated with code like that shown in Example 8-5.
Note that we save this method as Function.prototype.bind
, so that all function objects inherit it. This technique is explained in detail in 9.4.
// Example 8-5. A Function.bind() method for ECMAScript 3
if (!Function.prototype.bind) {
Function.prototype.bind = function(o /*, args */) {
// Save the this and argumetns values into variables so we can use them in the nested function below.
var self = this, boundArgs = arguments
// THe return value of the bind() method is a function
return function() {
// Build up an argument list, starting with any args passed to bind after the first one, and follow those with all args passed to this function.
var args = [], i
for (i = 1; i < boundArgs.length; i++) args.push(boundArgs[i])
for (i = 0; i < argmetns.length; i++) args.push(arguments[i])
// Now inovke self as a method of o, with those arguments
return self.apply(o, args)
}
}
}
Notice that the function returned by this bind()
method is a closure that uses the variables self
and boundArgs
declared in the outer function, even though that inner function has been returned from the outer function and is invoked after the outer function has returned.
The bind()
method defined by ECMAScript 5 does have some features that cannot be simulated with ECMAScript 3 code shown above.
- First, the true
bind()
method returns a function object with itslength
property properly set to the arity of the bound function minus the number of bound arguments (but not less than zero). - Second, the ECMAScript 5
bind()
method can be used for partial application of constructor functions. If the function returned by thebind()
is used as a constructor, thethis
passed tobind()
is ignored, and the original function is invoked as a constructor, with some arguments already bound.- Functions returned by the
bind()
method do not have aprototype
property (theprototype
property of regular functions cannot be deleted) and objects created when these bound functions are used as constructors inherit from theprototype
of the original, unbound constructor. - Also, a bound constructor works just like the unbound constructor for the purposes of the
instanceof
operator.
- Functions returned by the
The toString()
Method
Like all JavaScript objects, functions have a toString()
method. The ECMAScript spec requires this method to return a string that follows the syntax of the function declaration statement. In practice most (but not all) implementations of this toString()
method return the complete source code for the function. Built-in functions typically return a string that includes something like “[native code]” as the function body.
The Function() Constructor
Functions are usually defined using the function
keyword, either in the form of a function definition statement or a function literal expression. But functions can also be defined with the Function()
constructor. For example:
var f = new Function("x", "y", "return x*y")
This line of code creates a new function that is more or less equivalent to a function defined with the familiar syntax:
var f = function(x,y) { reutrn x*y }
The Function()
constructor expects any number of string arguements. The last argument is the text of the function body; it can contain arbitrary JavaScript statements, separated from each otehr by semicolons. All otehr arguments to the constructor are strings teh specify hte parameters names for the function. If you are defining a function that takes no arguemtns, you simply pass a single string — thefunction body — to the constructor.
Notice that the Function()
constructor is not passed any argument that specifies a naem for the function it creates. Like function literals, the Function()
constructor creates anonymous function.
There are a few points that are important to understand about the Function()
constructor:
- The
Function()
constructor allows JavaScript functions to be dynamically created and complied at runtime. - The
Function()
constructor parses the function body and creates a new function object each time it is called. If the call to the constructor appears within a loop or within a frequently called function, this process can be inefficient. By contrast, nested functions and function definition expressions that appear within loops are not recompiled each tie they are encountered. - A last, but very important point about the
Function()
constructor is that the function it creates do not use lexical scoping: instead, they are always compiled as if they were top-level functions, as the following code demonstrates:
var scope = "global"
function construcFunction() {
var scope = "local"
return new Function("return scope")
}
// This line returns "global" because the functio nreturned by the Fucntion() constructor does not sue the local scope.
constructFunction() // => "global"
The Function()
constructor is best though of as a globally-scoped version of eval()
that defines new variables and functions in its own private scope. You should really reed to use this constructor in your code.
Callable Objects
We learned in 7.11 that there are “array-like” objects are not true arrays but can be treated like arrays for most purposes. A similar situation exists for functions. A callable object is any object that can be invoked in a function invocation expression. All functions are callable, but not all callable objects are functions.
Callable objects that are not functions are encountered in two situations in today’s JavaScript implementation. First, the IE web browser implements client-side methods such as Window.alert()
and Document.getElementsById()
using callable host objects rather than native Function objects. These methods work the same in IE as they do in other browsers, but they are not actually Function objects. IE9 switches to using true functions, so this kind of callable object will gradually become less common.
The other common form of callable objects are RegExp objects — in may browsers, you can invoke a RegExp object directly as shortcut for invoking its exec()
method. This is a completely nonstandard feature of JavaScript that was introduced by Netscape and copied by other vendors for compatibility. Do not write code that relies on the call ability of RegExp objects: this feature is likely to be deprecated and removed in the future. The typeof
operator is not interoperable for callable RegExps. In some browsers it returns “function” and in other it returns “object”.
If you want to determine whether an object is a true function object (and has function methods), you can test its class attribute using the technique shown in Example 6-4:
function isFunction(x) {
return Object.prototype.toString.call(x) === "[object Function]"
}
Note that this isFunction()
function is quite similar to the isArray()
function shown in 7.10
Functional Programming
JavaScript is not a functional programming language like Lisp or Haskell, but the fact that JavaScript can manipulate functions as objects means that we can use functional programming techniques in JavaScript. The ECMAScript 5 array methods such as map()
and reduce()
lend themselves particularly well to a functional programming style. They are intended as mind-expanding exploration of the power of JavaScript’s functions, not as a prescription for good programming style.