Let's indent the code.
Code:
var StringBuilder = (function() {
var privateArray = new Array();
function privateAppend(str) {
privateArray[privateArray.length] = str;
}
return {
add: function(str) {
privateAppend(str);
},
value: function() {
return privateArray.join(“”);
}
}
})();
// First we show that the string is empty
document.write(“Our String: “ + StringBuilder.value() + “ < br / > ”); // “Our String: “
StringBuilder.add(“Super”);
StringBuilder.add(“Cala”);
StringBuilder.add(“Frajalistic”);
// Now we display the finished concatenated string
document.write(“Our String: “ + StringBuilder.value() + “ < br / > ”); // “Our String:
SuperCalaFrajalistic”
So, look at the StringBuilder variable. It's a function that gets executed upon definition, similar to this:
Code:
function alert_x() { alert('x'); }();
This just alerts 'x'. So what does the StringBuilder function do when it's executed? Well, first it defines to local variables: privateArray and privateAppend. The first is an empty array, the second is a function.
privateAppend is not called, it is merely defined.
Then, it returns an object. You may recognize {} from JSON (JavaScript Object Notation). So the last statement in the main function returns an object. In that object, there are 2 functions defined: add and value.
These functions are not called, they are merely defined.
So what's the point of all this weirdness.
Well, let's take a simpler example.
Code:
function alert_x() {
var x = 'x';
alert(x);
}();
alert(x);
The first section defines and calls a function called alert_x which alerts 'x'. The last line alerts the variable x which is undefined. But the alert_x function defines the x variable! Ok, so what's going on here? Nothing weird, you're probably familiar with local variables.
What you're probably not familiar enough with are the concept of functions as variables AND the concept of closures. Defining a local variable puts a name:value pair into the current context. That context is an object, known as a closure. Standard garbage collection states that if nothing references an object, delete it, but if something references it, leave it there.
Well, every object in javascript contains a reference to it's defining context. It's just that most people use only one context ever: the global context.
Code:
var x = 'x';
function alert_x() {
alert(x);
}();
Nothing wrong with this code right? The variable x is global, so every function can access it. Well, go one layer deeper.
Code:
var x = 'x';
function one_layer() {
var y = 'y';
function alert_y() {
alert(y);
}();
}
one_layer(); // alerts 'y'
alert_y(); // undefined function "alert_y"
In this code, we defined a function that defined a local variable AND a local function. That inner local function had
access to the local variables that were defined in the same context as itself. That is the essence of a closure. Now, how do you avoid the error on the last line?
Code:
var x = 'x';
function one_layer() {
var y = 'y';
return function() {
alert(y);
}
}
var my_func = one_layer(); // returns a nameless function
my_func(); // alerts 'y'
So, now I'm calling the inner function outside of the scope of the defining function. We already saw that local variables aren't accessible once the function has run, so why doesn't my_func() throw an error? Why is it able to resolve the variable y? Because my_func has a reference to it's defining scope, which contains a name:value pair for the variable y. One more example to drive it home.
Code:
var x = 'x';
var y = 'GLOBAL SCOPE'
function one_layer() {
var y = 'LOCAL SCOPE';
return function() {
alert(y);
}
}
var my_func = one_layer(); // returns a nameless function
my_func(); // alerts 'LOCAL SCOPE'
There is a scope chain. Whenever a variable is encountered during execution, it must be resolved. If your inside a function, the first place it looks is the local scope. The next place it looks is whatever the local scope says is its parent scope. Usually, that's the global scope, which is why you can defined vars all over the place and access them in any function. But if you define a function inside another function AND you get the inner function out (a closure) then the parent scope is the
defining scope, so all the variables defined locally by the parent function are available to the child closure, but no one else!
These are the mechanisms used to create public and private members in javascript objects. In your example, add() and value() are not called until the end. They are defined by StringBuilder() but not executed. The object returned by StringBuilder holds a reference to the scope it was defined in, that scope contains the two variables labeled "private".