Introduction
JavaScript "was born under the shadow of Java" - Brenden Eich
It is "the most misunderstood programming language in the world" - Douglas Crockford
JavaScript has been used so widely nowadays, nearly everywhere as long as you are using a web browser and surfing the internet, most of the websites have JS inside, and even server side - nodejs
According to http://langpop.com/, JavaScript is the fourth popular programming language in the world.
In this post, I am trying to use refined language to cover some foundations of JavaScript (many of which have confused quite a number of developers) including basic data types, comparison mechanism, functions, execution context, Variable Scope Chain, essence of Closure and anonymous function (lambda). Hope it can more or less help people have more fun and less frustration in the future JS coding.
Basic Data Types/Object Types
In JavaScript, there are 5 basic types: Undefined
, Null
, Boolean
, Number
and String
, integers boolean values and strings are accessed by value, that's different with many modern languages like C# (System.String
) or Python (PyStringObject
), string
s are objects and passed by reference. The JS code snippet below proves that string
is stored in the stack and passed by value.
var str = "a";
function strValTest(s) {
s = "b";
alert(s);
}
strValTest(str);
alert(str);
String
in JavaScript is also immutable just like many other languages, i.e., any change applied on an existing string
will create a new string
in memory and destroy the old one (this is still different from C# in which there is a String Intern Pool to store all String
values in managed heap). The code snippet below shows the difference between string
and String
:
var str1 = "A new String";
console.log("str1");
console.log(str1 instanceof String);
console.log(str1 instanceof Object);
console.log(typeof (str1));
var str2 = new String("A new String");
console.log("str2");
console.log(str2 instanceof String);
console.log(str2 instanceof Object);
console.log(typeof (str2));
Then you might have the question: how come string
instance has methods now that string
is value type? The answer is that in JavaScript there are corresponding Object
wrappers for the basic types: Number
, Boolean
and String
, they inherit from Object
and have their own properties and methods such as Number.toFixed()
and String.indexOf()
. A simple code snippet has been given below:
string str = "I am a JSer";
alert(str.indexOf("JSer"));
Essentially at the back end, JS interpreter will temporarily creates a new String
object and invokes its instance method "indexOf
", after the method call finished, the temporary String
object will be claimed, the process can be demonstrated as below:
string str = "I am a JSer";
var tmp = new String(str);
alert(tmp.indexOf("JSer"));
tmp = null;
Comparison
Comparison might be a very confused part in JavaScript, why? See the code below:
console.log(null == undefined);
console.log(null === undefined);
console.log(NaN == NaN);
console.log('5' == 5);
console.log('5' === 5);
console.log(false == 0);
console.log(true == 1);
console.log(null == 0);
console.log(typeof (null));
console.log(typeof (undefined));
Foo.prototype = {
constructor: Foo,
valueOf: function () {
return "Object Foo";
},
toString: function () {
return "Foo";
}
};
var foo1 = new Foo();
console.log("foo1 == foo2: " + (foo1 == "Object Foo"));
var foo2 = new Foo();
console.log("foo1 == foo2: " + (foo1 == foo2));
foo2 = foo1;
console.log("foo1 == foo2: " + (foo1 == foo2));
</professional>
Are you sweating? I did... So I read books and copied the paragraph below from <Professional JavaScript for Web Developers>.
- If an operand is a Boolean value, convert it into a numeric value before checking for equality.
- A value of false converts to 0, whereas a value of true converts to 1.
- If one operand is a string and the other is a number, attempt to convert the string into a number before checking for equality.
- If either operand is an object, the valueOf() method is called to retrieve a primitive value to compare according to the previous rules. If valueOf() is not available, then toString() is called.
- The operators also follow these rules when making comparisons:
- Values of null and undefined are equal.
- Values of null and undefined cannot be converted into any other values for equality checking.
- If either operand is NaN, the equal operator returns false and the not - equal operator returns true. Important note: Even if both operands are NaN , the equal operator returns false because, by rule, NaN is not equal to NaN.
- If both operands are objects, then they are compared to see if they are the same object. If both operands point to the same object, then the equal operator returns true . Otherwise, the two are not equal.
Function
In JavaScript, function is not only a traditional function but also an object, defining a function is actually defining a pointer to that function, and function is not only a traditional function but also an Object
. I wrote the code snippet below for better understanding:
function dummyFunc() {
this.DummyProperty = "Dummy Property";
console.log("Dummy func");
}
var tempFunc = dummyFunc;
dummyFunc = null;
tempFunc();
var dummy = new tempFunc();
console.log(dummy.DummyProperty);
Another very important point of functions is the parameters, in JavaScript, function's arguments are ALL passed by value, NOT reference even if the argument is an Object
, to prove this please see the code snippet below:
var person = new Object();
function setName(obj) {
obj.Name = "Wayne";
obj = new Object();
obj.Name = "Wendy";
}
setName(person);
alert(person.Name);
Execution Context and Variable Scope Chain
Execution context is an environment in which all JavaScript runs, if not specified, the context is usually global (window), or can be specified by invoking call/apply. In a lower level, when JavaScript interpreter starts executing a function, this function's execution context will be pushed into stack, then the function itself will be pushed into stack.
Code snippet picked from: http://www.nczonline.net/blog/2010/02/16/my-javascript-quiz/.
var x = 5,
o = {
x: 10,
doIt: function doIt() {
var x = 20;
setTimeout(function () {
alert(this.x);
}, 10);
}
};
o.doIt();
o.doIt = function () {
var x = 20;
setTimeout((function () { alert(this.x); }).apply(o), 20);
}
o.doIt();
A scope chain is a list of objects that are searched for identifiers appear in the code of the context. When a snippet of code is executing under its execution context, within the context a Scope Chain is formed with local variables at beginning and global variables at ending, JavaScript resolves identifiers within a particular context by climbing up the scope chain, moving locally to globally, if a variable cannot be found after traversing the whole Scope Chains, an error will occur. Inside a function, the very first variable in its Scope Chain is arguments
.
var name = "solarSystem";
function earth() {
var name = 'earth';
(function () {
var name = 'country';
alert(name);
})();
alert(name);
}
earth();
alert(name);
alert(blah);
Closure
Douglas Crockford: "JavaScript has closures. What this means is that an inner function always has access to the var
s and parameters of its outer function, even after the outer function has returned. This is an extremely powerful property of the language."
In JavaScript, a closure is formed when you nest functions, inner functions can refer to the variables present in their outer enclosing functions even after their parent functions have already executed.
Let's first take a look at a basic Closure:
function foo(x) {
var y = 2;
return function (z) {
console.log("x + y + z: " + (x + y + z));
}
}
var bar = foo(1);
bar(3);
To deeply understand closure, we must first understand function, Execution Context and Scope Chain I described above, so describe the code snippet above could be: foo
is defined as a function pointer, the function takes one parameter, so this parameter belongs to its Scope Chain when it is called in future, inside foo
, a local variable y
is defined with integer value 2
, so it is also in the Scope Chain, and finally it returns an anonymous function which takes one parameter z
, once foo
is called, it returns a pointer which points to this anonymous function, the entire process can be described in detail below:
- Prepare Execution Context for
foo
. - Scope Chain for
foo
will be formed, members on the chain: arguments
, y
, anonymous function. - Anonymous function is defined but not executed, when it is executed in the future, its own Scope Chain will also be formed at the lower level of
foo
's Scope Chain, members on the Chain: arguments
, z
, and the most important, foo
's Scope Chain will be retained for this anonymous function. foo
returns the anonymous function, a closure will be created, Scope Chains in the Closure will be retained unless program explicitly null
s it. Please note when foo
returns, the anonymous function inside it is not executed! - When executing bar passing parameter
3
, JavaScript interpreter will firstly search bar's Scope Chain and try to find x
, y
, z in
, z
is 3
but cannot find x
and y
, then it climbs up one level, and finds retained Scope
, x
's value is 1
, and y
's value is 2
(if not found it will climb up again, in this case, it will be global), ah-ha, we found all of them, result is 6
.
Clear? Not confused? I hope you are:) Simply saying, a Closure is a function that can access parent Scope Chain AND the Scope Chain is retained!
The code snippet below should help us to completely understand "retained Scope Chain":
function wayneClosore() {
var i = 0;
i++;
return function () {
console.log(i);
};
}
var closure = wayneClosore();
closure();
closure();
closure();
closure();
As soon as we create the new Closure - "closure
", its outer function's Scope Chain is retained for it (i.e. variable "i
" is stored in the Scope Chain with value 1
), so later on when this closure is executed, it will fetches the stored Scope Chian in which the variable "i
" was 1
, no matter how many times it executes, what the code above actually does is simply print out i
whose value is 1
, result will always be 1
.
So now that Scope Chain was retained, variables inside it could be changed, if I modify the above code as below:
function wayneClosore() {
var i = 0;
return function () {
console.log(++i);
};
}
var closure = wayneClosore();
closure();
closure();
closure();
closure();
Every time I executed "closure
", it fetches variable "i
" in the Scope Chain and keeps on increasing its value by i
each time.
In addition, if there is more than one inner function inside one function body, then the retained Scope Chain will be shared between each other, please refer to one more example below:
function shareScope() {
var n = 0;
return {
"innerFuncA": function () { console.log(++n); },
"innerFuncB": function () { console.log(++n); }
};
}
var shareScopeInstance = shareScope();
shareScopeInstance.innerFuncA();
shareScopeInstance.innerFuncB();
Delve deeper, essentially in ECMAScript, functions have an "Internal Property" - [[Scope]]
, ECMA-262 defines it as: A lexical environment that defines the environment in which a Function object is executed. As an instance, in the example above, when foo was executed and return value to bar, foo's Scope Chain was save into bar's [[Scope]] property.
Let's finally take a look at one example which might confused a lot of people, and then finish the Closure part.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
result.push(function () {
console.log(++i);
});
}
return result;
}
var fnlist = buildList([1, 2, 3]);
for (var idx in fnlist)
fnlist[idx]();
In the example above, the answer is NOT "1,2,3
", after buildList
is executed, result
is an array that contains n (n = 3) closures, all of them share the same Scope Chain created by buildList
, when each of them is executed, JS interpreter fetches the Scope Chain and looking for i
, what is i
'value in the Scope Chain? After the for
loop i
became 3 since JS has no blocked scope i
still exists outside of for
loop, and in the console you will see "4, 5, 6
" printed out.
Anonymous Function (Lambda)
As long as we understand Scope Chain and Closure completely, there will be no confusion about anonymous function, it is essentially a declared function without a function pointer points to it, it is always used to setup a "blocked-scope", for example, many JavaScript libraries were executed inside a big anonymous function:
(function (window, undefined) {
var VirtualCompany = function () {
};
})(window);
The anonymous function is executed as soon as it is downloaded completely, passing in the window object and only expose one global object: VirtualCompany
, so that the library encapsulates its internal implementation and won't conflict with other JS Libs.
By employing anonymous function, we can modify the Closure example I demonstrated above, to let it achieve our original goal:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
result.push((function (i) {
return function () { console.log(++i); };
})(i));
}
return result;
}
var fnlist = buildList([1, 2, 3]);
for (var idx in fnlist)
fnlist[idx]();
This time, the result will be "1,2,3
", because every time's invocation on "result.push
", what was pushed into the array? The answer is: A closure with the anonymous function's Scope Chain stored, what's inside the anonymous function's Scope Chain? i
in the for
loop. During each iteration inside the for
loop, an anonymous function is executed by passing i
into it, so i
exists in its Scope Chain, and since the anonymous function returns another anonymous function, a Closure was formed and the Scope was retained.
Summary
JavaScript is a great language and it has a very bright future in my humble opinion, considering its important role in the coming Web Standard - HTML5, event I/O based, high performance web server - nodejs, and JavaScript will also play an important role in the coming Cloud time, it is a time for the developers who didn't seriously learn it before to re-learn it. In fact, I have written broken JavaScript code for 6 years, however, to be honest, I am ashamed that I never seriously learnt it in the past, some of its basic theory, useful skills as well as best practices I never knew, so I wrote this post to summarize things I re-learnt. Hope it will help programmers like me.
Happy JavaScript coding! Be a happy JSer. :)
Further Reading (Strongly Recommended)