The Joy of Javascript
or... the joy of ECMAscript or Standard ECMA-262 Script
I may have realised I really liked javascript before I started
reading him, but it was only after I found Douglas Crockford's
articles on the subject that things perhaps crystallized.
I can sum up the point of my blog post here by quoting from one
of Douglas Crockford's articles:
"... JavaScript has closures. What this means is that an inner function
always has access to the vars and parameters of its outer function, even
after the outer function has returned. This is an extremely powerful
property of the language. There is no book currently available on
JavaScript programming that shows how to exploit it. Most don't even
mention it."
-- Douglas Crockford
From: http://www.crockford.com/javascript/private.html
This was written many whiles ago, so it's quite possible that there are
books that talk about this now - I don't know for sure...
Before I get back to the closure, I'm going to reel off some of the
things I like about javascript.
[0] Has the basics
A good scripting language should make it easy for you to wield
both normal index arrays and associative arrays (hashes, dictionaries
or whatever you want to call it). You can do a lot with just these
2 datastructures. It's the bread and butter of scripting.
Array behaviour:
var ar = [];
ar.push(1);
ar.push('foo');
Associative array behaviour:
var assoc = {};
assoc['foo'] = 'bar';
Javascript's
associative array ({}) is also it's basic object which you can
easily modify...
[1] Simple Objects - can be handy
A simple javascript object is easy as:
var obj = {};
This is the same as its associative array.
And you can add to it like this:
obj.param1 = 'some parameter';
This is the object-style of adding and amending
simple objects in javascript, using the dot operator.
This form is the same as
obj['param1'] = 'some parameter';
which is the associative array format.
Here is yet another way using object literals:
var obj = { param1: 'some parameter' };
Nice, light-weight, easy. No fuss.
It's an easy way to bundle some things together quickly.
[2] Simplicity and consistency
Javascript (ECMA-262) is a one-trick pony. All it's goodness is
built around one simple thing: the closure. In fact, pretty
much everything else it has is fairly ordinary or lacking.
Some languages have a feeling of more consistency than others.
On the "consistent" side we have languages like ruby/smalltalk
("everything is an object"), lisp (no syntax,
macros and lexical closures, "everything is a list" ), haskell
(pure/functional), javascript ("the closure, stupid!") etc etc
and on the other we have: perl, php and maybe languages like
visual basic 6 which feel a bit cobbled together or perhaps lack
magic or have too much crudely bolted on in some unexpected,
hard-to-read way.
So, what is javascript's one trick...
Javascript has closures and variables; a root Object and a
prototype system if you care to use it.
Behold the closure
var F = function() {}
That's it.
You can call it:
var f = F();
Closure as object
If you call F and it returns something that lives on in your
program and that refers to something defined locally inside of F()
,
then you have a closure. It's kind of like a forerunner to an object
instance (as recognised by a java or ruby programmer) because some state
and functionality lives on specific to that particular invocation of
F()
.
Here's a little pattern for creating object-like entities (I
might also refer to these as quasi-objects).
var F = function() {
me = {};
var secret = 1;
me.public_property = 2;
me.privileged_func = function() {
return secret;
}
return me;
}
var f = F();
f.privileged_func();
privileged_func
is privileged because it has access to the
secret
variable.
Every time we make a new f
, a new copy of privileged_func
will
be created. This isn't necessarily a problem but if want to
optimise, and your objects you can add functions outside of the
closure created by F()
.
F.methods = {
func1:functions() { return this.public_property; },
func2:functions() {....}
}
F.make = function() {
var n;
var f = F();
for(n in F.methods) f[n] = F.methods[n];
return f;
}
f = F.make();
Here we introduce F.make
which does the job of adding in
public functions to an instance of F()
. Note that these
public functions can refer to the object they have been
assigned to via the this
keyword. Above, func1
, accesses
the public_property
of an instance of F
.
Javascript also allows you to instantiate objects using the new
keyword. This creates object instances (in which case F is
referred to as a constructor and f as an 'object instance' often
shortened to 'object'). Instantiating with the new
keyword tells
javascript to create an object that uses javascript's prototype
system.
var f = new F();
Note that the function defined inside F will not be available.
A privileged function pattern for object instances is shown
near the end of this article.
Whether you work with F either calling it directly or with the 'new'
operator, you are still working with the same thing, that works
the same way: the closure.
And that's it. This is what makes javascript simple and sort
of consistent (to use this term very loosely): the closure,
the most protean thing known to computer science.
[3] Prototypically Overrated
It seems that javascript's coolness is often touted along the
lines of its prototype system or alternatively, that it can be
made to mimic classically object oriented programming languages.
Prototypes allow you to share functions across multiple instances.
We did this manually in the above example using F.make()
.
If you use the 'new' operator, you get this for free via javascript's
prototype system.
Prototypes are a good way to conserve memory and define functions
that get shared by all object instances created from
a constructor similar to F.make()
.
But prototypes are limited in the same way the F.make()
strategy is limited.
If those methods you add to the prototype need to access
the state of the object instance, that state will need to be
publicly available.
Prototypes and inheritance (prototype or classical)
My preference when writing a complex system of objects and
functions, is to hide all but the methods that an object is
prepared to expose to the world outside of it. Everything else
should be hidden using javascript's closures.
Not doing this is like asking your object to wear all it's innards
and internal organs on the outside for all to see and
run amok with as they choose.
Minimizing and making explicit the interface your objects,
quasi-objects and modules provide to the outside world, helps you
to more easily reason through your code and the systems
you construct with those objects and modules.
And it allows you to more easily re-implement an object
whilst maintaing its interface to said world.
(If you are wondering what is meant by "modules", this
is covered further down.)
You solve exposed state problem by not using prototypes and instead
adopting a privileged function pattern.
The prototype chain (prototype inheritance) is also touted as a
benefit; as javascript's version of classical inheritance.
Whilst this is useful, for code reuse (where you specialise an
existing object to solve a new problem), and for things like
convenience wrapper functions, overuse of inheritance (prototype
or classical) can make your code harder to read and reason through.
You not only have to think "horizontally" about the interfaces
that your objects and modules expose and how they are used; but
you also have to think "vertically" up a class or object
hierarchy.
Prototypes encourage you not to hide state and to potentially
build "vertical" hierarchies.
Prototype example
- Here's a basic prototype example where we set an object
'proto' which contains some useful methods as the prototype
for the F constructor.
privval
is not accessible by F's prototype object
F.prototype
- Only values defined through the
this
keyword in the F
constructor will be accessible to the functions defined in F.prototype
.
- These values are publicly available to anything that can see
or access an instance of F. So we can't hide the state.
- If you replace the anonymous object literal assigned to
F.prototype
with an object instance of another
constructor (say g = new G()
), you can start to build a chain
of prototypes. The methods in G.prototype will be available
to both f and g.
var F,f;
// Constructor F:
F = function() {
var privval = 1;
this.pubval = 'mess with me!';
}
F.prototype = {
meth1: function() { return this.pubval; }
}
f = new F();
f.meth1(); // returns this.pubval
f.pubval = "here's a change!";
Maker function pattern to the rescue
This article
appears to be suggesting that - through an extra
closure - we can encapsulate an object's state and still use a
prototype to access it. Basically, we use a closure courtesy of
calling a maker function, to encapsulate the two. I'm not
entirely sure at this point how it would be done.
[4] Half-way functional
Javascript sits between functional and object oriented
programming worlds. You can whip up anonymous functions and
pass them around with gay abandon. This type of thing can lead
to patterns and techniques that allow you to do powerful things.
If you understand the concept behind closures you'll start to
get the hang of it and you won't look back.
Closure gotchas
It can also get a little confusing. If an outer closure has a
variable that changes - say in a for-loop - and during each
iteration of the for-loop you generate a new inner function (not
closure) that will get executed later (eg as an event handler)
and which refers to something that changes with the for-loop (eg
the looping variable), you'll find all your inner functions will
refer to last item in the for-loop. Unfortunately, you need to
manufacture an inner closure and pass in the changing variable
into it so that a copy is taken and stored in that closure.
This inner closure returns the inner function which you can
then use. This type of thing can get expensive too.
Here's an example:
var cf1 = [];
var cf2 = [];
function createFuncs1() {
var i;
for(i=0;i<10;i++) {
cf1.push(function(){alert(i);});
// GOTCHA: 'i' refers to whatever state
// 'i' was left in the closure associated with
// createFuncs1.
// This will be 10.
}
}
// Add a closure to solve the problem...
function createFuncs2() {
var i;
for(i=0;i<10;i++) {
cf2.push(
function(i2){
return function(){alert(i2);};
}(i)
// i2 is a local variable of the inner closure
// that is created with the invocation using 'i'.
);
}
}
createFuncs1();
cf1[0](); // 10
cf1[1](); // 10
createFuncs2();
cf2[0](); // 0
cf2[1](); // 1
cf2[2](); // 2
Similarly with objects. You can mimick some of the things that
"real" OOP languages have if you really want to force it or you
can use javascripts prototype system - see the links and discussion above.
[5] The module pattern (variation on yahoo pattern)
Modules created using the module pattern are invoked
once (when your program is read in to the
interpreter), returning an object that gives access to
stuff living inside that closure. It can be pretty
much anything too. You can use modules created by
this pattern to store functions, variables and objects
(both the constructors and object instances). You can
make all of these public or keep them private. If you
want your module to keep state like an object with
some private variables, it can. If you want to stash
some functions in a namespace, you can use a module to
do that.
Here's one way to do it:
var Module1 = function() {
var module = {};
var private1 = ... ;
module.public_function1 = function() {
...
}
module.PublicObject1 = function() {
...
}
var private_function1 = function() {
...
}
function PrivateObject1 {
...
}
...
return module;
}();
// Later...
var obj1 = new Module1.PublicObject1();
var result = Module1.public_function1();
// I'm adopting a rubyish convention of
// camel-casing classes (constructors in
// javascript speak) and underscoring other stuff.
// Whatever floats your boat.
- Module1 is a global variable which is assigned the
result of
function(){}()
- local variable 'module' refers to an empty object
(
{}
) which we proceed to fill
- public functions and object constructors are added
to
module
; you could of course add variables to
module
as well
- private variables, functions and objects are
created without being added to
module
; they stay
hidden within the scope of Module1
closure
module
is returned at the bottom to the outside
world - in this case to the global Module1
variable
(GOTCHA #1)
- Don't forget to execute
function(){...}()
otherwise Module1
is just a refernce to the function
itself (GOTCHA #2)
[6] The privileged function pattern
The term privileged comes from Douglas Crockford
who clearly demonstrated it.
The idea is that you can create private state in
your object instances by using local variables inside
the constructor.
A privilege function example was shown near the
beginning of this article using f where f = F()
.
The example below is one way to use this pattern
for instantiated javascript objects.
By adding a function to the this
variable inside
the constructor (or, in the example below I've
assigned this
to me
and used this instead),
this function, which acts like a public method, gets
access to these local variables which would otherwise
not be accessible (when you instantiate an object
instance). In the example below, accessor1
is an
example of a privileged function which can access
local variables priv1
and my
in the closure
formed by an instance of Object1
:
var Object1 = function() {
var me = this;
var priv1 = 'priv1';
var my = {};
// Private state. You don't have to do this.
// Just define functions and variables as normal
// if you prefer.
me.accessor1 = function() {
...
// accessor1 is a public method but can also
// access 'priv1' and 'my' above.
}
// You can also say 'this.accessor1 = ... '
my.init = function() {
...
}
my.init();
}
// Create an instance of Object1.
var obj = new Object1();
var result = obj.accessor1();
accessor1
can see everything inside the of
function() that is referenced by Object1
.
me
is a reference to the object (this
); there
are potentially siuations where this
can change so
we stash it in me
and use me
ever after; you can
probably get by with this
in many cases if you
prefer
- the use of
my
is not part of the pattern; it
is something I often do to distinguish from me
for
private stuff that the outside world cannot see.
Matter of taste. You could just use something
like the priv1
variable.
- the
init()
function is not part of the pattern,
but demonstrates defining and using a private
function in this case to group all the
initialisation instructions together to run at the
end of the constructor when you instantiate
an instance of the object.
Concluding Remarks
Judicious use of javascript's closures gives you
surprising scope to build complex applications.
Using the module pattern and privileged functions and
closures in general you have plenty of scope to build
complex behaviour within the browser and on the server
using frameworks like rhino.
There's plenty of scope here to talk about
conventions - how to organise and package code often
using the above two patterns. I have written a
standard that tries to set this out which I
might revisit. You can find it here:
http://js.web17.com.au/specs/packaging-and-namespacing/index.xhtml
You can find some of my javascript code at:
http://github.com/danielbush