Introduction
In this guide, I have tried to put together all important nuts and bolts of JavaScript. I had a really hard time learning JS, since all these parts were scattered over the net. I gradually found them over the years. I hope I have covered all of them. Please let me know in the comments if you feel I missed something important or if something is incorrect.
Irrespective of the title, this guide should be helpful to all developers. However, you should be familiar with the basics of JS before this guide could be helpful. This is more of a collection of JS concepts.
Loosely Typed
Yup! No scratching your head to decide if this should be float
or double
, int
or short
or long
or even a String
. Just declare the variable using – var my_variable;
and be done with it. This is easiest to grasp concept of the lot.
Always ‘use strict’;
You can use the magic phrase "use strict";
at the start of a function or a JS file to turn on strict mode.
Just like any other language, JS language’s syntax, semantics and APIs are first proposed in a language specification document; which when approved is ready to be implemented by all browsers. This helps ward off incompatible implementations by different browsers and makes our, JS developers’, life easier. What a plague of incompatibility looks like can be seen in section ‘DOM is not JS’.
For JS, the language specification document is named – ECMAScript. The current version of JS we see in our modern browsers is based on ECMAScript 5. This specification describes a stricter version of JS. Frankly, the non-strict version of JS allows and encourages extremely sloppy and bad coding practices, which at the end of the day will result into one hell of a messy product. The “strict
” mode is much more cleaner, which all self respecting JS developers should be aware of and must use.
A full list of restrictions is available on MDN, but the most important one I feel is that all variables must be declared before use
in strict
mode.
So,
function () {
'use strict';
return a;
}
will result into error in strict
mode, provided a
is not already defined globally. In non-strict
mode, it will merrily create a variable a
in global scope and continue. More about scopes later. However, I can give you a demonstration of this.
function f1() {
a = 12;
return a;
}
function f2() {
return a;
}
alert(f1());
alert(f2());
Try running this code on jsfiddle.net. In both alerts, you will see 12
. This is because in both the functions, a
is in global scope.
Another important point to remember is that once strict
mode is enabled, it can’t be disabled. Strict
mode can be specified for a particular JS file, where that is enabled for all code in that file; or, it could be enabled selectively inside functions.
Semi-colon is Not Optional
You must have read somewhere before that in JS, semi-colons at the end of a statement is not required. However, this does not mean that there is absolutely no requirement of semi-colons. The language interpreter actually tries to guess where a semi-colon should have been and you missed it, and continues like that. I loath this “feature” a lot. Sometimes, this results into hard to find bugs and this is allowed in strict mode too. >:(
function avg(a, b) {
return
(a + b) / 2
}
console.log(avg(2, 3))
What will be printed on the console? It will be undefined
! An unambiguous language like C or Java makes no error here but JS does because of its overzealous attempt to “guess” when you need a semi-colon. In this case, JS “sees” this code as:
function avg(a, b) {
return;
(a + b) / 2;
}
console.log(avg(2, 3));
However, if you wrote it like:
function avg(a, b) {
return (a
+ b) / 2
}
console.log(avg(2, 3))
Then, the result would have been correct. In this case, if JS tries to insert semi-colon after (a
, then that will result into invalid syntax as the (
needs to be closed.
Hoisting and Scoping in JS
Unlike C, C++ or Java, JS has only two simple scope types – Global and Function. So, in JS, for
, if
, while
, etc. do not define a scope block. So, a code like:
function () {
if (someCondition) {
var a;
}
}
is actually treated as:
function () {
var a;
if (someCondition) {
}
}
This behavior of JS is also known as hoisting. Like hoisting a flag, it hoists all variable declarations to the topmost line in that scope.
Take another example:
function test() {
'use strict';
console.log(a);
console.log(b);
var a = 10, b = 10;
console.log(a);
console.log(b);
}
test();
This is a unique example. The output in this case will be:
undefined
undefined
10
10
However, if you uncomment the line console.log(x)
, you will end up with error – ReferenceError: x is not defined
. This is because a
and b
are hoisted to top of function so they are present when console.log
statement is run, however they were yet to be assigned values.
function test() {
'use strict';
console.log(a);
console.log(b);
var a = b = 10;
console.log(a);
console.log(b);
}
test();
Notice the var
line. In this case, not only the console.log(b)
lines will error out, but also the var
line, provided the outer scope doesn’t already have variable b
defined. This is because in this case, b
is part of an expression, so var
does not define b
, but only a
.
All variables defined outside the function blocks (be it in JS files or <script>
blocks) are in global scope. Since there is only one global scope, so they are all accessible everywhere.
About Functions
Functions are Also Objects
I will re-state this point throughout this guide. This is important to understand. The functions are objects of type Function
. Like any other objects, they too have methods! And like any object, they too can be defined anywhere, returned from other functions or passed as arguments, etc.
Take this example:
function gen() {
return function ans(factor) {
return 2 * factor;
};
}
Does it look confusing? If yes, then let us substitute the returned function by a variable.
function gen() {
return f;
}
Looks better? Since functions are just mere objects, we can do any of the following:
function gen() {
var f = function ans(factor) {
return 2 * factor;
};
return f;
}
Or:
function gen() {
function ans(factor) {
return 2 * factor;
};
return ans;
}
The Assignment Analogy
When you name a function (taking function f
as example), like:
function f(factor) {
return 2 * factor;
}
then that is almost equivalent to:
var f = function (factor) {
return 2 * factor;
};
I say ‘almost’, since even though…
f(2);
var f = function (factor) {
return 2 * factor;
};
...will error out saying – TypeError: f is not a function
, since f
is actually undefined
. But,…
f(2);
function f(factor) {
return 2 * factor;
}
...will not error out. Since, similar to var
, function definitions too are hoisted.
Functions are Not Polymorphic
You cannot name two functions the same and hope to invoke either of them based on the parameter types. The function defined later will overwrite the previous. After all, if you take the assignment analogy, then the next function definition is going to reassign the same variable with its own function
object.
function ans(f1, f2) { ... }
function ans(f1) { ... }
But the point to note is, all arguments in functions are always optional.
function ans(a, b) {
}
ans(2);
Function Return
In a function, you may choose to return any data or nothing at all.
function () {
if (cond1) {
return {
a: 10
};
} else if (cond2) {
return;
} else if (cond3) {
return 1;
}
}
What if all the conditions fail? Similar to cond2
, this will return undefined
.
Closure
JS has the power of lambda. Simply put, lambdas are anonymous functions. This has proved to be one of the core pillars of the language. Now this has been introduced even into Java 8.
All functions in JS also have access to the outer scope, be it another function or global. It is also able to retain the outer scope even after the outer function has finished execution. This concept of hanging onto the outer scope is closure.
Java developers will be familiar with the concept of final
. Where anonymous inner classes have access to final
variables in outer scope and they hang onto it. This is like closure, but not 100%, since closure requires that the whole of outer scope be captured. Although JS interpreters optimize their implementation and only hold-on to the variables actually being referred. Also in true closure, you are allowed to update values of variables in outer scope.
With this knowledge, can you guess the output of the following?
function adder(factor) {
return function (a) {
return a + factor;
};
}
var adder2 = adder(2);
console.log( adder2(5) );
If you guessed 7
, then that is correct. adder2
variables refers to a function generated by adder
which always adds 2
to any number passed to it.
If you find it difficult to understand, then this is what adder2
actually is:
adder2 = function (a) {
return a + 2;
};
Now make a guess for the following.
function gen() {
var i = 1, f = [];
for (; i <= 2; i++) {
f.push(function (a) { return a + i; });
}
return f;
}
var fs = gen();
console.log( fs[0](5) );
console.log( fs[1](5) );
If your answer is not 8
and 8
then it is wrong! fs[0]
and fs[1]
return the functions generated inside gen
‘s for-loop. Remember that in this case, both these functions hold-on to the same outer scope, not the values of i
. When the for
-loop ends, the value of i
is 3
. So, both the functions are adding 3
to 5
, and not 1
and 2
to 5
.
Truthy and Falsy
Much like C and C++, but unlike Java, JS has a wide range of what can be treated as truthy or falsy. All objects (except empty string
) and non-zero numbers are treated as truthy. Whereas empty string
, zero, null
and undefined are treated as falsy.
undefined
is a special value. All variables when not assigned any value have the value undefined
. Clear? Similarly, all functions which do not return a value actually return undefined
. In fact, it is a keyword. So, the following code is valid:
var a = undefined;
This is actually equivalent to:
var a;
Value Coercing
In JS, when you try to do something impossible with values, then JS tries it best to make them compatible and come up with some meaningful result.
For example: !0
is actually boolean true
since !
can work only with boolean values. 0
when coerced into boolean is false
. '2' * 1
is actually number 2
, since *
cannot work on strings. But, '2' + 1
is string 21
, since because of the presence of one string
, the number is coerced into string
.
Here is a tip. Since, !0
is true
. You can use this for a neat trick – var hasChildren = !!children.length;
. This will set hasChildren
to appropriate pure boolean value.
Prototype Based Programing
Unlike C, C++ or Java, functions in JS are actually objects and, as an OOP developer would say, instance of a class Function
. However, there are no classes in JS, just constructors. The constructors create objects by cloning another object. So, all functions in JS are clones of Function
. Only functions are allowed to be constructors, i.e., new
operator can be applied only on them.
In the words of Douglas Crockford: you make prototype objects, and then … make new instances. Objects are mutable in JavaScript, so we can augment the new instances, giving them new fields and methods. These can then act as prototypes for even newer objects. We don’t need classes to make lots of similar objects….Objects inherit from objects. What could be more object oriented than that?
JS support two kinds of object creation – by cloning existing object (using Object.create(otherObj)
) or ex nihilo (“from nothing”, using Object.create(null)
). By the way, {}
is the short-hand for Object.create(Object.prototype)
, and []
is short-hand for new Array()
.
Actually, Object.create(obj)
creates a new object (think of it like an empty shell) where obj
is its prototype (this gives content to that shell). So, it doesn’t actually clone obj
; instead it sets obj
as its prototype. As its literal meaning, a prototype is an object from which the main object derives its properties and methods. However, you are allowed to directly add any property or method to the main object too.
Object.prototype
itself is an ex nihilo object which other objects inherit, including Function.prototype
. The prototype
property in objects in itself is an object and can have other prototype forming a chain. More on this later. The standard way to get the prototype of an object is using Object.getPrototypeOf(obj)
. However, IE8 and below do not implement this. The non-standard way (also not supported by Internet Explorer) is using __proto__
property. For Internet Explorer and others, you can use obj.constructor.prototype
.
new Operator
You can guess it. Similar to Java, new Foo()
will create a new object of type Foo
. When I say it is type of Foo
, it means the object has the prototype set to Foo.prototype
. As you will recall, you can do the same using Object.create()
too. So, new Foo()
is almost equivalent to Object.create(Foo.prototype)
. I say almost, since in former case Foo
function is executed before the created object is returned. In the latter case, the Foo
function is not executed.
What is this?
This is one of the primary points of confusion for new JS developers. In JS, the function is always executed in some context, implicit or explicit. That context decides the value of this
inside the function. The same function can be invoked with any explicit context. When the context is not specified, then it is ‘window’ for non-strict mode and undefined
for strict mode. You can use the following code to test this:
function A() { return this; }
A();
function B() {'use strict'; return this; }
B();
Have a look below:
var car = new Car();
car.honk('loud');
When you uses the new
operator, you created a new object of type Car
. When you did car.honk('loud')
, first JS interpreter looked into car
object for honk
method, if it did not find that there, then next it would look into Car.prototype
object for this method. If the method was not even there, then it would look into Car.prototype.prototype
object and so on. Once the method is located, that method would be triggered in the context of car
object. That means, in that method then, this
will be car
. These behaviors are part of JS language.
Recall that functions are themselves objects of type Function
, which means they too have methods and can in-turn be used as objects! Functions have a method call
using which you can explicitly specify the context in which to execute the function.
Car.prototype.honk.call(someObj, 'loud');
This will call honk
such that this
inside that refers to someObj
. Practically, someObj
could be any object, not necessarily objects of type Car
.
There is also an apply()
method in Function
class. The only difference between this and call()
is that, here the second argument is an array of the arguments we need to send to the called function.
In the next section, we will put this information to use.
Prototypal Inheritance is Way Different from OOPs
In class based inheritance, the compiler takes care of making the inheritance work for you automatically. However, in prototypal inheritance (JS), it is left to developers to fend for themselves. Actually, prototypal inheritance is a concept/trick developed by developers and is not something the language defines.
The main feature we expect in inheritance is the ability to inherit methods and fields from parent classes, and we should be able to override them if required.
Let’s now try to mimic this behavior in JS.
function Vehicle(color) {
this.color = color;
}
Vehicle.prototype.honk = function() {
alert('Honking!');
};
function Car(color) {
Vehicle.call(this, color);
}
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.getWheelsCount = function() {
return 4;
};
function Autorickshaw(color) {
Vehicle.call(this, color);
}
Autorickshaw.prototype = Object.create(Vehicle.prototype);
Autorickshaw.prototype.getWheelsCount = function() {
return 3;
};
Above, the Vehicle.call(this, color)
statement executes the Vehicle
function in the context of current object and passes on the color
param. This way, we technically made a super()
call. So, this.color
is like a field variable and this.honk()
and this.getWheelsCount()
are methods.
The prototype chain formed in this case is:
Car.prototype -> Vehicle.prototype
Now, there is lots of boilerplate code above. Let’s try to cut that.
function define(superClass, definition) {
function Class() {
if (superClass)
superClass.apply(this, arguments);
if (definition.initialize)
definition.initialize.apply(this, arguments);
}
if (superClass)
Class.prototype = Object.create(superClass.prototype);
var proto = Class.prototype;
for (var key in definition) {
proto[key] = definition[key];
}
return Class;
}
var Vehicle = define(null, {
initialize: function(color) {
this.color = color;
},
honk: function() {
alert('Honking!');
}
});
var Car = define(Vehicle, {
getWheelsCount: function() {
return 4;
}
});
var Autorickshaw = define(Vehicle, {
getWheelsCount: function() {
return 3;
}
});
The define
method is pretty straight forward. Although, before I continue, note the arguments
keyword. This magic variable is available inside a function. This is an “array” of all the arguments supplied to that function, when it is invoked. I say array in quotes, since this is not really a standard JS array. It has only few features and methods of an array.
This function internally defines another function which is the new class we are defining. One quick thing to note is that the function’s name is Class
. That means to define its instance, we should write new Class()
. However, the name we write against the new
operator has no significance. The name is just a reference to the action function object. So, if A = B = Class
then new A()
or new B()
or new Class()
will all yield the same result.
The function then iterates over the provided singleton object and simply copies them with the same keys to Class
‘s prototype
. Finally, it returns that function – Class
. Astute readers will notice that define
feels similar to PrototypeJs’ Object.extends()
.
Now let’s add some features to define
.
function define(superClass, definition) {
function Class() {
if (superClass) {
this.$super = superClass.prototype;
superClass.apply(this, arguments);
}
if (definition.initialize)
definition.initialize.apply(this, arguments);
}
if (superClass)
Class.prototype = Object.create(superClass.prototype);
var proto = Class.prototype;
for (var key in definition) {
proto[key] = definition[key];
}
return Class;
}
We just now added a this.$super
, which we can use to access super methods like we do in other languages.
var Vehicle = define(null, {
initialize: function(color) {
this.color = color;
},
honk: function() {
alert('Honking!');
}
});
var Car = define(Vehicle, {
honk: function() {
this.$super.honk();
alert('Beep Beep');
}
});
You may ask, how to mimic private
methods? Well we simply don’t. We prefix method name with _
, denote that it is private
. Convention is simpler than enforcing a rule. However, if you really want to enforce that, then there is another way to define a class.
function Vehicle(color) {
var that = this;
this.color = color;
function _showColor() {
alert(that.color);
}
this.honk = function() {
_showColor();
alert('Honking!');
};
}
This will give an effect similar to what we have used till now, with the added benefit of private
methods. Notice that we defined a variable that
. This was needed so that _showColor()
could refer to it. It simply can’t use this
, since that has a special meaning.
In this approach, we use the power of closures. However, you will notice that this is not as performant as the previous approach, since a new function instance will be created per object of type Vehicle
. Let’s how Car
can inherit this.
function Car(color) {
Vehicle.call(this, color);
this.getWheelsCount = function () {
return 4;
};
}
Car.prototype = new Vehicle();
Here is the key difference. This type Car
‘s prototype is not Vehicle.prototype
but object of Vehicle
.
The prototype chain formed in this case is:
Car.prototype -> new Vehicle() -> Vehicle.prototype
There is still another way to define a class.
function Vehicle(color) {
this.color = color;
}
Vehicle.prototype = {
honk: function() {
alert('Honking!');
}
};
Here, we replaced the default prototype object with another object. Nothing changed, but this is more convenient to read and type.
ECMAScript 6 has a proposal to support class
and extends
keywords. Finally, in the future, we might get actual classes support in JS.
The instanceof operator
Java developers will instantly recognize this. Same as in Java, this operator evaluates to true
if the object on the left is of a given class type (given on right). Its syntax is:
object instanceof function
This will make JS follow the prototype chain of object
to look for function.prototype
. So,
console.log(car instanceof Car);
console.log(car instanceof Vehicle);
console.log(car instanceof Autorickshaw);
But,
var o = Object.create(null);
console.log(o instanceof Object);
This is false since o
is ex nihilo object and is not an object of anything. So you can say, it is just a shell. This too can be used as map, similar to {}
, which is an object of type Object
.
JS in Browser is Single Threaded
JS language does not dictate that the interpreter be single threaded, in fact, many server-side interpreters like nodeJs are not. However, the browser interpreters are single threaded. (Modern browsers now support Web Workers API which can launch background threads.)
This is an important fact to keep in mind. So, no matter when the Ajax call completes, the callback you provided for that is not going to fire unless you are not done with what you are doing right now.
Also note when JS is processing, the browser gets locked up. It can’t respond to user inputs at that instant. So, if you have long running JS job, then possibly the browser will stop responding. If you have a modern browser, then use Web Workers for that or divide your task into smaller chunks and periodically invoke setTimeout()
to give control back to browser before continuing with your next chunk of task. I have a project CInk, which does some heavy rendering task; where I use this trick.
DOM is Not JS
The magic objects we usually see in browser JS, like – window
, document
, etc. are DOM objects defined by the browser. DOM stands for Document Object Model. This is a tree data model to represent the HTML code. These objects are injected by browser into JS domain, and they are not defined by JS language specification. DOMs have their own specification – Document Object Model (DOM) Level 1, 2 and 3. This specification was formed after ECMAScript was standardized1.
Unfortunately, DOMs have a wide list of APIs which vary from browser to browser. For a short list, you can see DOM Core on QuirksMode.
However, I am not here to talk about what is DOM. I only want to hammer in the point that DOM and JS are separate things. So, when coding on server-side JS, do not expect these DOM objects. You might find other magic global objects there.
Concluding
I hope this helps, and happy coding.