JavaScript as a functional programming language
Paul E.S. Wormer
November 2011
println()
.
The output generated by the
the call of println()
is shown in
green in the right margin of the snippet. The output
order is determined by the order of the invocations of println()
.
A JavaScript function looks much like a function in C. The only syntactic
difference between a C function and a JS function is that JS functions do not have a
return-type (there are no int, double
, etc., functions) and instead has the keyword
function
. Obviously, the absence of a return-type is a consequence of JS being a
weakly-typed language. More in particular, the syntax of a JS function is the following
function any_name(p1, p2, ...){ statements }
any_name
and the variables p1
, p2
stand for formal
parameters. Parameters may be anything: primitive
values (numeric constants, strings, etc.) or any kind of compound objects (including functions and
arrays). As in
C, a return statement is optional. When an early return is needed a return statement can
be
entered that gives control back to the caller. When a value must be returned, the return statement
is necessary: return expression
returns the value of expression
. Without return
statement a JS function "falls off the end" when it reaches the terminating curly bracket, it then
returns the JS value undefined
.
As in C, internal (local) variables are automatic, they are created on entry and destroyed on
exit, so that they are in existence only during execution of the function. JavaScript does not
have static internal variables; if internal
variables have to retain their value between calls they must be placed in the lexical
environment
of the function, see below. In Fortran 77 all variables, including local variables, are
static, and
hence preserve their values between successive calls (in theory the Fortran statement SAVE
is required for this, in practice SAVE
is redundant).
A JS function is more than a function in C: it is an object comparable to all other objects of JS; this is expressed by referring to JS functions as first-class objects, entities with properties and values. Another important property is that functions may be nested in other functions. In addition to the ordinary—C-type—properties, a JavaScript function has the following characteristics:
Instead of the function statement given above, a function may be created by assignment of a
function expression to a variable on the left-hand side. Usually a function in a function
expression is anonymous (unnamed). In the next example a function expression is assigned to
hiThere
. Its body consists of one print statement only. The difference between registering
and invoking a function must be noted. The following statement registers the function but does
not invoke it:
var hiThere = function (your_name){ println("Hi there "+ your_name); };
hiThere("Paul");
hiThere = (function(your_name){ println("Hi there "+ your_name); }("Paul"));
g
and h
in the next example. Invocation of
h
returns the function f
that is not yet called.
A call of g
gives invocation of f
. The latter function writes a string and, in
absence of an explicit return statement, returns undefined
, a value that is then also
returned by g
.
var f = function(x){ println(x); }; var g = function(z){ return f(z);}; var h = function(y){ return f; }; tmp = g("XYZ"); // -> "XYZ" h("XYZ"); // No output println(tmp); // -> undefined
A function in a function expression is allowed to carry a name:
var f = function any_name(){ ... };
Consider, as an example of the dynamic definition of a JS function, the product of three functions mappings ℝ → ℝ, functions on the real line): f1(x), f2(x), and f3(x). The product p(x) is written as
// Define the functions var f1 = function(x){ ... }; var f2 = function(x){ ... }; var f3 = function(x){ ... }; // Create the product function p: var p = prod(f1, f2, f3); // Invoke the new function p for argument value x println(p(-1)); // prod may be used more than once, for instance different order: var q = prod(f3, f1, f2);
p
or q
in the example) from existing functions. One would have
to write new static code that must be compiled.
f
without formal
parameters is invoked as f()
.
The reader is challenged to predict the output of the following lines of
legitimate JS
code (and don't look in the right margin where the answer is given in green):
var f = (function(x){ return function(){ println(x);}; }(1)); f(2);
f(2);
is omitted, does
not write anything at all (not even 1)?
var f = (function(x){ return function(){ println(x);}; }(1));
(1)
defines
a function without parameters that is assigned to f
. The function bound to f
has
x=1
in its lexical environment (f
is a closure). However, the new function is not
yet called. This is done in the line f(2)
. To understand this line we must note
that the number of actual arguments may larger than the number of formal
parameters, the redundant arguments are ignored. Hence, f(2)
is equivalent to the call
f()
. The latter call, absent in the second code snippet, triggers the
actual printing of x=1
.
function host(...){ ... function nested(...){ ... } ... }
Nested JS functions have access to variables of their outer
(host) function. Nested functions are not visible outside their host function,
i.e., their scope is restricted to their enclosing function.
Consider the next snippet of JavaScript code.
The variable x
is local to function host
,
but not to function nested
. However, x
is
in the lexical environment of nested
, meaning that
nested
has full read/write access to it. The statement host()
invokes host
.
The function nested
is simple, all it does is print x
and add one to x
(the
operation ++ adds one and stores the updated x
). Note that invoking nested
outside
host
generates an error.
function host(){ var x = 0; function nested(){ println(x++); } nested(); nested(); } host(); // Invoke host nested(); //Try to invoke nested => "nested is not defined"
function host(x){ function nested(){ println(x++); } nested(); nested(); } host(0); // Invoke host while setting x = 0
function host(x){ var nested = function(){ println(x++); }; nested(); nested(); } host(0); // Invoke host while setting x = 0
function host(...){ var x, y, z, ... ... var nested = function(){ ... } ... return nested; } var fnc = host(...); // Invoke host; bind nested to fnc fnc(); // Invoke nested. Note that host has finished at this point /* At this point both host and nested have finished, * x, y, z, ... still exist and retain their values. * Their values are only accessible through invocation of nested. */
fnc
, external to host
, is bound to the returned
function nested
. The binding persists (meaning that nested
stays alive) after host
has finished execution. Invocation of fnc
is
equivalent to invoking nested
.
The local (internal) variables x, y, z, ..
of the host form the lexical environment
of the inner function nested
. The inner function
has read/write access to the local variables of its host. (In this context the
term "lexical" refers to the fact that the environment can be deduced from the
source statements.) A closure is defined as a function augmented with its
lexical environment. In other words, a closure consists of a function,
originally defined within a host function, with access to variables x, y, z, ..
local to
the host. The latter variables, i.e., those in the lexical environment of the inner
function, are sometimes referred to as the upvalues of the closure. The name is somewhat
awkward because sometimes one must consider "the values of the upvalues". However, in lack of a
better terminology, "upvalue" will be used.
In summary, a closure is a special kind of object that combines two entities: a function, and the lexical environment in which the function was defined (the set of upvalues). Upvalues stay alive, and keep their value, after the host function has finished execution. They also stay alive after the returned function has finished execution. In contrast to upvalues, local variables of returned functions are automatic, they disappear after returned functions are exited.
Closure will be explained in more detail by several examples.
In the first example g
(a function) is returned and assigned to
h
.
function f(){ var str = "QRS"; var g = function(){ println(str); str = str + str; // double str }; return g; } var h = f(); // h bound to value of g (a function) h(); // call h, prints "QRS" and modifies str h(); // call h => "QRS"+"QRS" f(); // Make new copy of str; does not affect h h(); // call h => "QRSQRS"+"QRSQRS" (old copy of str) h = f(); // Reassign h h(); // => "QRS"
h()
invokes function g
that prints and modifies the
upvalue str
. The second call h()
prints the modified value of str
(and modifies it again, a third call of h
prints "QRSQRS"+"QRSQRS"
,
the operator +
concatenates strings). As far as the function
f
is concerned, the variable str
is local, all f
can do is reset
it. Since str
is automatic, a new variable str
is created that, however, is not
an upvalue of h
. Only when the closure h
is reassigned, the upvalue is
reassigned.
The name of the internal (nested) routine is irrelevant, there is not even a reason to assign it to
a local variable (to g
in the previous example). The function may be returned directly:
function f(){ var str = "DEF"; return function(){ println(str); str = str + str; }; } var h = f(); h(); h();
f
may be omitted and replaced by an unnamed function (if it is used only
once), but do not forget to call the unnamed function:
var h = (function(){ var str = "KLM"; return function(){ println(str); str = str + str; }; }()); // call unnamed function on rhs h(); h();
h
does not receive the function as value, but the result of the invocation of the
function.
The value of the variable h
can be printed. Note in the following example that its value is
the (anonymous) function in an earlier example assigned to g
.
println(h)
str
may be promoted to parameter:
var h = (function(str){ return function(){ println(str); str = str + str; }; }("IJK")); // call unnamed function on rhs h(); h();
Although the previous examples seem pretty trivial, they show the most
important characteristics of JS as a
functional programming language: the value of a local (named or unnamed) variable may be a
function, this value
can be returned and bound to an external variable (in the examples above h
).
The upvalues of the returned function (in the examples: str
) remain accessible, both for
reading
[as in
println(str)
] and for modifying (as in str = str + str
).
var f = function(x){ var g = function(){return x;}; // definition g x = 2; return g; // returning g }; var h = f(1); println(h());
h
(bound to g
) prints the value that x
possesses just before g
is returned, the answer is choice 2: the value of the local
variable at the time that host function f
returns the value of g
and h
is
bound to the value of g
. Being local to the host, the variables in the lexical environment
(in this case consisting only of x
) of the nested function g
may change values
during execution of the host function. A snapshot of the values is taken at the time the host gives
control back to the calling program after which the host function has lost control over its local
variables.
Above it was discussed that local variables are automatic, they are created upon entry and
destroyed upon exit. This is also true for local variables of a function that is part of a closure.
To show the difference in lifetime between a local variable and an upvalue
we first introduce some information about JS boolean expression:
The statement var a
creates the local variable a
and gives it the JS
value
undefined
.
The value undefined
is a legitimate JS value that can be handled like any value. In
particular, when it appears as a boolean, it has the value false
.
Furthermore, the exclamation mark !
negates, that is,
the value of a
will be printed in the following snippet:
var a; if (!a) { println(a); }
a
obtains a value (other than the number 0, the null string, or undefined
),
it will return true.
var a = "a string"; if (a) { println("a true " + a); } else { println("a false " + a); }
We are now ready to see the difference between the lifetime of an upvalue (upval
) and a
local value (locval
).
The statement in the following fragment: var locval
gives
locval
the value
undefined
. Every time nested
(bound to f
) is executed this value is
assigned.
Similarly, every time host
is executed upval
obtains the value undefined
.
function host(){ var upval; function nested(){ var locval; if (!upval){ upval = 'Persistent'; locval = 'Volatile'; } println(upval); println(locval); } return nested; } f = host(); f(); println(" "); f();
host
is called nothing is printed yet, because the printing is done inside nested,
however upval
(with value undefined
) is added to the closure nested
(which is bound to f
). When f
is called for the first time (!upval)
returns true
and both
upval
and
locval
obtain a string value. When f
is called the second time
(!upval)
returns false
, because upval
still possesses the string value
Persistent
, the two assignments are skipped, and the value of
locval
stays equal to the value it obtained in the statement var locval
.
Let us next consider two different closures, nested in the same host, that are returned
simultaneously
and address the question: do they share the values of the variables in their (shared) lexical
environment?
To find the answer, we let two nested functions change the
variable
i
in their lexical environment in two different ways,
var f = function(i){ var func_arr = []; func_arr[0] = function(){i = i + 10; return i;}; func_arr[1] = function(){i = i + 100; return i;}; return func_arr; }; var c = f(0); // array of two functions var a = c[0]; var b = c[1]; println(a()); // prints effect of invoking a println(b()); // prints effect of invoking b println(a()); // prints effect of invoking a again println(b()); // prints effect of invoking b again
a
and b
,
share the value of i
. Closures returned simultaneously from a host share their
upvalues.
The final question is: what happens if a closure is returned more
than once, or, in other words, the enclosing function is executed more than once.
Do all objects bound to this closure access the same
set of values of variable(s) in the lexical environment of the nested function? Or is a new set of
variables (holding different values) created every time a closure is assigned to a new variable?
In fact, since the var
statements in the body of the enclosing function are executed more
than once, it is to be expected that different closures with different lexical environments are
formed. This is also true when the upvalue is a function parameter and a var
statement is
optional.
In the next example, the left-hand sides
a
and b
are bound to a (nested, anonymous) function that updates the variable
i
in the lexical environment offered by the enclosing function f
. First
a
is invoked three times, increasing the value of i
from 0 to
2. Then b
is invoked, it starts counting anew and returns 0, so that evidently the answer
is: b
accesses its own private copy of
i
and does not touch the copy of i
belonging to a
:
f = function(i){ return function(){return i++;}; }; a = f(0); b = f(0); println(a()); println(a()); println(a()); println(b()); println(b());
a
and b
share the same nested function, they are different
because their values of variables in the lexical environment are different.
The reader is invited to inspect the following two snippets of JS code that both
fill an array func
with functions. Element func[i]
is to contain a function that
returns the value i
. Both snippets end with the
statement println(func[5]())
supposedly writing 5. Can you understand why the first time
the number 5 is written correctly and the second time incorrectly the number 10?
var i; var a = function(i){return function(){return i;};}; var func = []; // new (empty) array for (i=0; i < 10; i=i+1) { func[i] = a(i); } println(func[5]()); /* Second snippet: */ var a = function(){ var func = []; for (i=0; i < 10; i=i+1) { func[i] = function(){return i;}; } return func; }; func = a(); println(func[5]());
i
at the time that the nested function(s)
is/are returned.
To end this section, we summarize the findings of this section as the following rules:
g
nested in host f
is returned from f
and bound to a
variable h
external to f
, all values of all variables in the lexical environment
(upvalues) of
g
are available to h
, even after the outer function f
has finished
executing. this
this
is a function variable depending on the context in which the function
is called. If
this
appears in
the body of a function that is invoked "stand-alone", not as a method of an object, this
receives the name of the global object. In a web browser the global object is
named window
.
If this
appears in a function that is invoked as an object method then
this
refers to the object (receives the name of the object). The keyword this
in an inner
function, nested within a hosting method, (and not itself invoked as method by the hosting method),
refers to the
global object, in the same way as a "stand-alone" function.
In the following example fnc
is a function nested in
obj.method
. Note that the print statement (marked with P) in obj.method
writes the
value of obj.s
, while the print statement (marked with Q) in fnc
writes the value
of window.s
:
var obj = {}; obj.s = "Object string"; window.s = "Window string"; obj.method = function(){ println("P: " + this.s); var fnc = (function(){ println("Q: " + this.s); }()); // fnc invoked "stand-alone" by hosting method }; obj.method();
this
in a nested function is according to Crockford
(The good parts) a mistake in the design of JavaScript.
A mistake or not, guessing the value of this
in a nested function is tricky
business. After a nested function is returned from its host, the keyword this
may
or may not refer to the object, even when ostensibly this
belongs to the body
of the method. The value referred to by this
requires close attention, mistakes are easily
made. To exemplify this, two code snippets will be introduced that are almost the
same, but yet give completely different results. The first snippet is:
/* First code snippet */ var obj = {}; obj.s = "Object string"; window.s = "Window string"; obj.method = function(){ println("P: " + this.s); return function(){ println("Q: " + this.s); }; }; obj.method()();
this
in the first print belongs to the body of obj.method
and this
in the second print belongs to the nested function.
However, consider the next snippet which is almost, but not quite, the same.
Notice that this
in the first print statement still seems to belong to the body of
obj.method
and
yet is bound to window
, while this
in the second print still appears to
belong to the nested function and yet is bound to obj
:
/* Second code snippet */ var obj = {}; obj.s = "Object string"; window.s = "Window string"; obj.method = (function(){ // Evaluate rhs as "stand-alone" function println("P: " + this.s); // before assigning the return value to lhs return function(){ println("Q: " + this.s); }; }()); obj.method(); // inner function is method of obj
obj
models a HTML
element that is the target of an event. The call: obj.method()
corresponds to the firing
of
method
upon the occurrence of an event on obj
.
The last snippet does what a web programmer would expect, provided he or
she is not aware of the problems associated with the use of
this
in nested functions. In web applications this
, appearing in an
event handler, may be expected to refer to a target HTML element and
not to the global window
object.
Let us consider the two code snippets above in more detail.
In the second snippet obj.method
is assigned a function:
obj.method ← function(){ println("Q: " + this.s); }
this
refers to obj
during execution of object.method()
.
In the following statement occurring in the second snippet:
obj.method = (function(){ println("P: " + this.s); ... }; }());
obj.method
. To remind the reader of this an extra pair of dummy parentheses is added.
During this evaluation,
this
is not bound yet to any object and then, by default, it is bound to window
.
In the penultimate example (the first code snippet), the order of calling triggered by the
expression
obj.method()()
is as follows. First the rightmost pair of parentheses invoke:
obj.method = function(){ println("P: " + this.s); ... };
obj
, so that this
gets the value obj
. Then,
because the expression returned:
function(){ println("Q: " + this.s); }
window
). Because the latter function does not return an explicit
value (it "falls off the end"), it returns undefined
as final value of the total expression. A small
modification of the first code snippet proves this.
var obj = {}; obj.s = "Object string"; window.s = "Window string"; obj.method = function(){ println("P: " + this.s); return function(){ println("Q: " + this.s); }; }; tmp = obj.method()(); println("tmp: " + tmp);
fnc
has many parameters, x1, x2, ...
, that are
constant during most of its calls and a few, y1, y2, ...
, that vary between calls.
It is then useful to turn the function into a closure that has x1, x2, ...
in its lexical
environment and y1, y2, ...
as formal parameters. Schematically,
var host = function(x1, x2, ...){ return function(y1, y2, ..){ ... }; }; // Assign x1, x2, .. var x1 = ..., x2 = ... // Fix x1, x2, ... inside fnc: fnc = host(x1, x2, ..); // From here on fnc can be called with different arguments y1, y2, .. // The arguments x1, x2, .. are preserved until reassignment of fnc. fnc(y1, y2, ...);
In the following example the
function prt
prints four variables, the first two being constant. They are taken out the
parameter list of prt
and introduced as parameters of host.
var host = function(x1, x2){ return function(y1, y2){ println(x1); println(x2); println(y1); println(y2); }; }; prt = host("Wild", "horses"); prt("won't ", "drag you away"); prt("are a ", "nuisance"); prt("aren't shot ", "I hope?");
An alternative way of currying is by means of the JS function-method call
. If f
is a function, f.call(null, x1, x2, y2, y2)
calls f
with arguments x1, x2, y1,
y2
. [The first argument replaces this
by null
everywhere in f
].
Because a function is an object, a method (curry
) may be defined that executes the function
through call
. Given a function (f
in the next example), it is possible to define
the method curry
such that the function f
is not affected, i.e., it does not have
to be rewritten. Schematically,
var f = function(x1, x2, y1, y2){ println(x1); println(x2); println(y1); println(y2); }; f.curry = function(x1, x2){ var that = this; return function(y1, y2){ that.call(null, x1, x2, y1, y2); }; }; g = f.curry("Cuddly", "cats"); g("are", "sweet"); g("must be ", "petted");
Along similar lines it is possible to define a general curry method applicable to any function. The
JS method apply
is similar to call
, but takes an array of arguments. The place
for a general curry function is as prototype
of the built-in object Function
.
Function.prototype.curry = function(){ var that = this; var args1 = arguments; return function(){ var args = []; var i; for (i=0; i < args1.length; i++) { // first set of arguments args[i] = args1[i]; } for (i=0; i < arguments.length; i++) { // second set of arguments args.push(arguments[i]); } that.apply(null, args); }; }; // Trivial example function: function fnc(x1, x2, x3, x4, y1, y2){ println(x1+' '+x2+' '+x3+' '+x4+' '+y1+' '+y2); } var g = fnc.curry('A', 'B', 'C', 'D'); g(1,2); g(5,6);
x
seconds" with x
counting down from
x = 10, 9, ...
down to 0
with intervals of one second (1000 msec).
After 10 seconds, countdown ends and *** FIRE *** is printed.
(To restart the countdown hit the refresh key F5 of your browser.)
function host(x){ var a = document.getElementById("fire"); var f = function(){ a.firstChild.nodeValue = "Will fire in " + x-- +" seconds"; }; var id = setInterval(f, 1000); return function(){ clearInterval(id); a.firstChild.nodeValue = "*** FIRE ***"; }; } var clear = host(10); //call host with x=10; assign clear setTimeout(clear, 10100); //cancel infinite loop calling f
setInterval
, clearInterval
, and setTimeout
. setInterval(f,msec)
makes a copy of
f
and
executes the copy every msec
millisecond. Because x
is in its lexical environment,
the copy of f
can update and print x
. The method setInterval
continues forever, but returns a process identification number (id
) by which the process
may be canceled. host
and assigned to clear
has access to id
and invokes the browser method
clearInterval
which cancels the process with number id
.
The closure clear
is called by setTimeout
after 10100
milliseconds, hence after f
has been executed ten times. write.document
(called in println
) can only be used if a web
page is built line for line. Here, every second some info is written in the middle of the text,
while the remaining part of this document has already finished writing. This is why the DOM method:
firstChild.nodeValue
is used for the actual writing. The first child of a HTML
element <span id = "fire">
is overwritten. For more details, see Flanagan.
In Fortran, with its static internal variables and its possibility to assign variables at compile
time, precomputing is easy. One defines a boolean variable, call it first
, internal to the
function and one sets first
at compile time equal to true. When the function is called for
the first time, first
is true: the expensive data are precomputed, stored in an internal
(static) array, and first
is set to false. Following entries into the function find the
static variable first
to be false and skip accordingly the precomputation.
In JavaScript one may use closure. Suppose for the sake of example that computation of the
mathematical function cosine (in JS: Math.cos
) is expensive and that its values are
only needed in function
f
for certain angles, say between 0 and π in steps of π/10. For readers whose
knowledge of trigonometry is rusty: cos(0) = 1, cos(π/2) = cos(90°) = 0, and cos(π) =
cos(180°) = −1. As is usual on computers, the
number zero will not appear as zero, but as a small number: x ⋅ 10−17,
which is printed as x e−17.
var f = (function(){ var cos = [], i, angle, step = Math.PI/10; println("setting cos"); for (i=0, angle = 0; angle <= Math.PI; angle=i*step){ cos[i++] = Math.cos(angle); } return function(k){ println("cos["+ k*step*180/Math.PI + "] = " +cos[k]); }; }()); // assign return value f(0); f(5); f(10);
f
receives the return value of the anonymous
function, not the anonymous function itself. The
array cos
, in the lexical environment of f
, is set when the anonymous function is
invoked, which happens only once. Note also that the string
setting cos
is printed once, indicating that the computation of the cosines happens
indeed only once. The function f
is assigned the nested function that prints the
kth value.
prod
. It has three functions
f
, g
, and h
as parameters and returns a function that is the product of
the three arguments. The parameters are in the lexical environment of the returned function and
hence are constant.
function prod(f,g,h){ return function(x){ return f(g(h(x))); }; }
prod
returns a function of x
.
In the next example two mathematical triple-product functions will be defined,
named p
and q
.
Recall that
var f1 = function(x){return Math.exp(x);}; var f2 = function(x){return Math.cos(x);}; var f3 = function(x){return Math.sqrt(x);}; var pi = Math.PI; // Define two different triple-product functions: var p = prod(f1, f2, f3); var q = prod(f1, f3, f2); var t; // temp for printing // Invoke and print them: t = p(0); println( t.toPrecision(6) ); t = p(pi*pi/4); println( t.toPrecision(6) ); t = p(pi*pi); println( t.toPrecision(5) ); t = q(0); println( t.toPrecision(6) ); t = q(pi/2); println( t.toPrecision(6) );
Recall that the first derivative of x2 is 2x and
of
ex it is ex. The latter function is printed as
comparison and gives an indication of numerical accuracy.
// Define the differentiation function with // global variable h as step size. var h = 1.e-3; function deriv(f){ return function(x){ return (f(x+h)-f(x-h))/(2*h); }; } // Define functions to be differentiated: var sq = function(x){return x*x; }; var exp = Math.exp; // and their derivatives (primed functions) var sqPrime = deriv(sq); var expPrime = deriv(exp); // Invoke and print derivatives println(sqPrime(0).toPrecision(6)); println(sqPrime(1).toPrecision(6)); println(sqPrime(2).toPrecision(6)); println(' '); println(expPrime(0).toPrecision(6)+' ' + ' '+ exp(0).toPrecision(6)); println(expPrime(1).toPrecision(6)+' ' + ' '+ exp(1).toPrecision(6)); println(expPrime(2).toPrecision(6)+' ' + ' '+ exp(2).toPrecision(6));
h
was introduced as global variable. It can also be made part of the
derivative function by currying. A new enclosing function with h
as parameter is
introduced and a closure deriv
with upvalue h
is returned.
function derivative(h){ return function(f){ return function(x){ return (f(x+h)-f(x-h))/(2*h); }; }; } var deriv = derivative(1.e-3); // As before : var sq = function(x){return x*x; }; var sqPrime = deriv(sq); println(sqPrime(2).toPrecision(6));
param
private to the object myContainer
. The latter object is equipped
with the priviliged method service
that it receives as method during execution of
the constructor function Constructor
. A privileged method is able to access private
variables and methods, and is itself callable by the outside world.
When object
myContainer
is constructed (by means of the operator new
), the constructor
function Constructor
is executed with the keyword this
having the value
myContainer
. Hence
during execution of Container
, the priviliged method myContainer.service
becomes a closure that has access to the inner function dec
and the local variable
secret
. The value of param
cannot be modified (other than by reconstruction of
myContainer
) and it can be read three times only. A fourth attempt of reading returns
Too many attempts
. This is shown in the next code snippet in which an effort to modify
param
by a call Container('DEF')
clearly fails.
function Container(param) { var secret = 3; function dec() { if (secret > 0) { secret--; return true; } else { return false; } } this.service = function () { if (dec()) { return param; } else { return "Too many attempts"; } }; } var myContainer = new Container('ABC'); println(myContainer.service()); println(myContainer.service()); println(myContainer.service()); println(myContainer.service()); Container('DEF'); println(myContainer.service());