Article Series
Introduction
Part 1 of this series showed a little bit of CSS 3 and JavaScript in action. Now we see a bit more of device side 'ninja' i.e., JavaScript. We will see challenges and solutions in the perspective of devices with limited battery life, computing power, and storage.
Figure - Where you want to run JavaScript?
JavaScript Today
JavaScript is a powerful language and people write code in various styles. There is a lot of script around, and script reuse is important to progress forward, may it be coming from Brendan Eich, Douglas Crockford, Ryan Dahl, or anyone else. To merge existing frameworks, it is important that we understand various styles in use.
Figure - Left to Right: Brendan Eich creator of JavaScript, Douglas Crockford major contribution in YUI and JSON, and Ryan Dahl creator of Node.js
Before I go further, whenever you find a badly explained example, I recommend playing with it in jsfiddle! :)
A Class and Object
In JavaScript you declare a class by declaring its constructor function which is 'nothing' more than a 'regular' JavaScript function.
function Button() {}
It is a convention in JavaScript community to use Title Case for a constructor function.
The above code declares two things: a class Button
, and its constructor function.
With this function (or constructor function if you still wish to differentiate), you can create
Button
objects using the new operator:
var button1 = new Button();
var button2 = new Button();
A Class is just a regular function
The code above will allocate two memory locations and executes code inside
the Button()
function twice. As mentioned earlier, under the hood
Button
'class' or 'constructor function' is nothing but a 'regular' JavaScript function. In fact 'every' JavaScript function is an instances of the
Function
class.
var add = new Function("a", "b", "return a + b");
add(2, 6);
function addFactory() {
var addMethod = function (a, b) {
return a + b;
}
return addMethod;
}
var x = addFactory();
alert(x(1, 2));
In fact, all objects in JavaScript are descended from Object
; and inherit methods and properties from
Object.prototype
, although they may be overridden.
typeof Button;
typeof button1;
button1 instanceof Button
Constructor with parameter
You can have a constructor with some parameters just like regular JavaScript function:
function Button(text) {
}
Class Assets - Methods and Members
A class has two kinds of assets: members and methods. In following examples, we will progressively add private,
privileged, public and static assets in a
Button
class. We will also take a first look at inheritance and access rules.
public
To have public members of class:
function Button(text) {
this.text = text;
}
var button = new Button("Submit");
button.text;
private
And to have a private member i.e., one that is inaccessible in public methods:
function Button(text) {
var visible;
}
privileged
And to add privileged method i.e., one that can access private members:
function Button(text) {
var visible;
this.show = function() {
visible = true;
};
}
It is important to remember that private members are declared and accessed without using
this
.
private method
To add private method hide()
:
function Button(text) {
var visible;
function hide() {
visible = false;
}
}
public method
And to add a public method resetText()
and public member
rounded
:
function Button(text) {
this.text = text;
var visible;
this.show = function() {
visible = true;
};
function hide() {
visible = false;
}
}
Button.prototype.rounded = false;
Button.prototype.resetText = function() {
this.text = "";
}
The key difference between public and privileged is that,
a privileged method is created for every instance of the class while
a public method is shared between all instances of
a class. This becomes important if thousands of objects are created. We will see more on this later.
static
And to add a static member or method:
function Button(text) {
...
}
Button.defaultText = "My Button";
Button.resetDefaultText = function() {
Button.defaultText = "";
};
inherit (or chain)
Figure - JavaScript search for method or member in all chained objects
As mentioned earlier, every JavaScript class is just a regular function. We also know, every function in JavaScript is an instance of
the Function
class.
The Function
class has a public property prototype which is used to chain objects together. When
a child object is chained to a parent object, all non-private parent assets behave as if they are the part of
the child object. If a method or member is not available in a child object, JavaScript looks into its parent prototype and this search continues until an object with prototype = null is found. Checkout this example:
function A() {
this.num = 100;
}
function B() {
}
function C() {
}
B.prototype = new A();
C.prototype = new B();
var x = new C()
x.num;
x.speed;
You can see that chaining could be used to create inheritance as we will see below. But before we do that, I would like to point out that deep chaining could
affect performance due to search. Also for a non-existent member, search will continue till the end of chain.
inherit using new
This example show how to inherit from Button
class using
new
:
function Button(text) {
this.text = text;
}
function LinkButton(text, url) {
Button.call(this, text);
this.url = url;
}
LinkButton.prototype = new Button();
var b = new LinkButton("Google", "http://www.google.com");
b instanceof Button;
b instanceof LinkButton;
b.text;
inherit using Object.create()
Figure - A large object could be initialized later using Object.call(this)
The new
operator not only creates Button
object but also calls its constructor. In
the example above, the Button
constructor is called twice, once at
new Button()
and then Button.call(this, text)
. This is a waste of CPU cycles. A better way is to use
Object.create()
, which only creates the object but does not call constructor. You could call
the base class constructor later using Object.call()
.
function Button(text) {
this.text = text;
}
function LinkButton(text, url) {
Button.call(this, text);
this.url = url;
}
LinkButton.prototype = Object.create(Button);
var b = new LinkButton("Google", "http://www.google.com");
b instanceof Button;
b instanceof LinkButton;
b.text;
Nesting vs. prototype
It is perhaps timely to mention that, the code inside the Button
constructor should be short (and execute fast) to reduce object creation time. Consider a case of creating hundreds of
LinkButton
objects and adding them to a list on a smart phone. On
a smart phone, a quick object creation + initialization will eat less CPU cycles and result in increased battery life, which is usually 6 to 10 hours!
Figure - Running JavaScript on iPhones with 6 to 10 hours of battery life
Beside battery drain due to CPU usage, a function nested inside 'constructor function' is created for every new instance of class which will tax memory.
Figure - Typical RAM in smart phones
As nested functions are created for every object construction, keep public assets (i.e., methods and members) and
initialization outside the constructor function whenever possible. This will keep object construction fast and footprint low.
function Button(text) {
this.text = text;
}
Button.prototype.resetText = function() {
this.text = "";
};
function Button(text) {
this.text = text;
this.resetText = function() {
this.text = "";
};
}
Declare method inside constructor only if it is accessing private class assets or if the constructor is initializing a member with supplied argument. For all other cases use prototype to add class assets.
Figure - Recent smart phones are providing 'desktop-grade' CPU
Although recent smart phones are providing 'desktop-grade' CPU but nesting creates a level of scope and slows down name resolution.
Class & Object - Add or Remove Assets On-the-fly
Figure - JavaScript is like a flexible free-size T-shirt
Although a class in JavaScript acts as a 'blue print' for object creation, however you can add methods and members even after creating objects. 'on-the-fly' class asset is
immediately added to 'all live' objects of that class while 'on-the-fly' object asset is only added to 'that particular' object instance only. Consider for example:
function Button(text) {
this.text = text;
}
var b1 = new Button("Tooltip Button");
var b2 = new Button("Normal Button");
b1.toolTip = "This tooltip public member is only for object 'b1'";
b2.toolTip;
Button.prototype.resetText = function() {
this.text = "";
};
b1.resetText();
b2.resetText();
Figure - Adding Class Asset - Add Stereo System in class Car
Just like you can add assets to class or object, you can delete them too.
delete b1.toolTip;
Button.prototype.resetText = undefined;
b1.toolTip;
b2.resetText();
Figure - Removing Object Asset - Remove Alarm System from object yourCar
On the fly asset management may be useful for composing a large object spread across many JavaScript files inline with Asynchronous Module Definition (AMD). Even for smaller objects, composition might be useful for example device capability detection and 'trimming' object as necessary.
Seal and Freeze Object (Not Class)
Figure - JavaScript provides various ways to stop on-the-fly asset add and remove
When we are talking about JavaScript, JScript, and ECMAScript are they 1 language or 3? I wanted to touch this subject in
brief, so to safely say ECMA Script 5 has 'a way' to turn off on-the-fly asset add and remove in objects.
JavaScript is a Netscape and now Mozilla backed language while JScript is a similar version developed by Microsoft. In 1996 both Microsoft and Netscape agreed to develop their version of language based on ECMA standards, called ECMA Script or ES for short.
In ES5 (or JavaScript 1.8.5) compatible browsers, one can Seal()
and Freeze()
an object. You can not add or remove property in a Sealed object while Freezing adds to sealing, by making property value un-editable. If you only want to stop adding new properties, but existing one could be deleted use
preventExtensions()
. Seal, Freeze and Prevent Extensions are not applicable to Class. Consider examples below:
function Button(text) {
this.text = text;
}
var b = new Button("My Button");
Object.seal( b );
Object.isSealed( b );
b.text = "Submit";
b.toolTip = "Mine";
delete b.text;
Button.prototype.toolTip = "Mine";
Object.freeze( b );
Object.isFrozen( b );
b.text = "Submit";
delete b.text;
Button.prototype.toolTip = "Mine";
Object.preventExtensions( b );
Object.isExtensible( b );
b.text = "Submit";
delete b.text;
b.toolTip = "Mine";
Button.prototype.toolTip = "Mine";
Hashtable nature of Assets
Figure - JavaScript properties are like key-value pair
JavaScript properties could be declared using "index" notation, "key" notation and "dot" notion. All are useful constructs in different context. See example below:
function Button(text) {
this[0] = text;
this["is enabled"] = true;
this.focused = false;
}
var b1 = new Button("My Button");
b1[0];
b1["is enabled"];
b1.focused;
that
Figure - 'that' is a way to give 'this' to nested functions
In JavaScript, private methods can not access privilaged assets. Following will not work:
function Button(text) {
this.text = text;
function isEmpty() {return this.text === ""; }
}
To provide necessary access, JavaScript community uses a work around using a var 'that
'.
function Button(text) {
this.text = text;
var that = this;
function isEmpty() {return that.text === ""; }
}
A more complete example might be:
function Button(text) {
this.text = text;
var that = this;
function isEmpty() {return that.text === ""; }
this.getText = function() { return isEmpty() ? "NO TEXT" : this.text; };
}
var b1 = new Button("");
b1.getText();
ASI - Automatic Semi Insertion
Figure - You can write most of JavaScript without semi
JavaScript treats 'every line' as a new statement except for few cases. What this means, you don't have to add semi at the end of every line as JavaScript automatically do this job. This is called ASI or Automatic Semi Insertion. So following is a perfectly valid script:
function Button(text) {
this.text = text
var that = this
function isEmpty() {return that.text === "" }
this.getText = function() { return isEmpty() ? "NO TEXT" : this.text }
}
var b1 = new Button("")
b1.getText()
For return, throw, break, or continue JavaScript automatically inserts semi for new line.
function sum(a,b) { return
a+b }
sum(1,3)
throw
"an error"
var day=0
switch (day)
{
case 0:
x = "Sunday"
break
case 1:
x = "Monday"
break
case 2:
x = "Tuesday"
break
case 3:
x = "Wednesday"
break
case 4:
x = "Thursday"
break
case 5:
x = "Friday"
break
case 6:
x = "Saturday"
break
}
alert(x)
continue
Following are the places where JavaScript treats a new line as a continuation of previous one, so JavaScript do not auto insert semi for these cases:
function onclick(
e)
var states = [
"NY", "MA", "CA"]
var states = {
name : "Newyork", code : "MA"}
var states,
cities
from("state").
where("id='MA'").
select()
var i = 1, j=5
i
++
j
for(var i = 0; i < 2; i++)
alert("hello")
alert("world")
var i = 0;
while(i++ < 2)
alert("hello")
alert("world")
var i = 0;
do
alert("hello")
while (i++ < 2)
var i = 0
if(i == 0)
alert("hello")
var i = 1;
if(i == 0)
alert("hello")
else
alert("world")
Objects without a class
Figure - Objects without Type
JavaScript is one the language in which you can create objects without class. The simplest of them is an 'empty' object i.e. one that does not have any assets. You can add assets in an empty object anytime or you can declare object with assets.
{}
in JavaScript represents an empty object or empty object literal.
var b={};
Now add some properties:
var b={text:"My Button", visible:false};
b.text;
And add a method:
var b = {text:"My Button", visible:false, show: function() {this.visible = true;} };
b.show();
b.visible;
You can return an object from function using object literal notation:
function getButton() {
return {
text: "My Button",
visible: false,
show: function () {
this.visible = true;
}
}
}
getButton().text;
You can nest objects as well:
var toolbar = {
button1: {text: "My Button 1"}
,button2: {text: "My Button 2"}
}
toolbar.button2.text;
Namespace - Avoiding Name Collision
As you can expect your script will be running with other scripts, therefore it is important to keep your code in a namespace. So far, we have wrote all our classes in a global scope. Let's create few namespaces.
var com = com || {};
com.Acme = com.Acme || {};
com.Acme.UI = com.Acme.UI || {};
com.Acme.UI.Button = com.Acme.UI.Button || {};
Now we have com.Acme.UI
namespace ready and Button
class added, its time to add class assets. But first note that you can not create constructor
function Button()
in a global namespace. So checkout some interesting things you can do with
anonymous function.
function () {}
The above will fail as:
- JavaScript does not expect an anonymous function.
- JavaScript expects both left and right hand sides of a statement.
On a different note, the simplest form of JavaScript statement is:
;
Let's surround anonymous function with parentheses to make it a statement.
(function () {});
Let's get back to our Button
class.
com.Acme.UI.Button = (function () {});
If we add parentheses at the end of the anonymous function, it will execute immediately.
com.Acme.UI.Button = (function () {}());
Now we have anonymous 'container' that will hold our class. Let's add bells and whistles.
com.Acme.UI.Button = (function () {
}());
com.Acme.UI.Button = (function () {
function Button(text) {
}
}());
com.Acme.UI.Button = (function () {
function Button(text) {
}
return Button;
}());
com.Acme.UI.Button = (function () {
function Button(text) {
this.text = text;
}
return Button;
}());
com.Acme.UI.Button = (function () {
function Button(text) {
this.text = text;
var that = this;
function isEmpty() {
return that.text === "";
}
this.getText = function () {
return isEmpty() ? "NO TEXT" : this.text;
};
}
Button.prototype.resetText = function () {
return this.text = "";
}
return Button;
}());
var b = new com.Acme.UI.Button("My Button");
b.getText();
b.resetText();
b.getText();
Library or Module
You can create a JavaScript library that spanning on multiple files using an 'anonymous container' used in previous section. Library or Module provides a scope where you can have both public and private members.
var LIBRARY = (function () {
});
var MODULE = (function (jQuery, underscore) {
}($, _));
We can create simple Util library as shown below.
var Util = (function (u) {
var key = "secret";
function hash() {
return 123;
}
u.version = "1.0";
u.productName = function () {
return "Acme Library";
}
return u;
}(Util || {}));
Util.productName();
Following is another way to hide private state and export public methods. Note that an object is returned when
anonymous function is invoked.
var Counter = (function () {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function () {
changeBy(1);
},
decrement: function () {
changeBy(-1);
},
value: function () {
return privateCounter;
}
}
})();
Counter.increment();
alert(Counter.value());
Chaining Methods
Chaining methods enable you to call methods on an object one after the other.
If you are not returning anything from method return this
.
var obj = {
value: 1,
increment: function () {
this.value += 1;
return this;
},
add: function (v) {
this.value += v;
return this;
},
shout: function () {
console.log(this.value);
}
};
obj.increment().add(3).shout();
obj.increment();
obj.add(3);
obj.shout();
Clean getter and setter
JavaScript provides a clean syntax for property get and set method through
get
and set
.
var carStero = {
_volume: 0,
get volume() {
return this._volume;
},
set volume(value) {
if (value < 0 || value > 100) {
throw new Error("Volume shold be between 0-100");
}
this._volume = value;
}
};
try {
carStero.volume = 110;
} catch (err) {
alert(err.message);
}
Globals
Figure - Minimize the use of Globals space
You can declare global variable very easily:
var width = 100;
height = 50;
delete width;
delete height;
var states = ["CA", "NY", "MA"];
delete states[1];
states[1];
states.length;
When the delete operator removes an array element, that element is no longer in the array and accessing it returns undefined. However it is still addressable.
Any member without var
goes to global scope.
height;
function Button(text) {
var label = caption = text;
caption = text;
}
ECMAScript 5 Strict Mode
John Resig has provided an excellent
explanation of
ES5 a strict mode. Here is what he has to say:
Figure - strict mode basically helps in reducing code writing errors
"use strict";
In strict mode, deleting a variable, a function, or an argument will result in an error.
var foo = "test";
function test() {}
delete foo;
delete test;
function test2(arg) {
delete arg;
}
Defining a property or function argument more than once in an object literal will cause an exception to be thrown.
{
foo: true,
foo: false
}
function (foo, foo) {}
Virtually any attempt to use the name 'eval
' is prohibited - as is the ability to assign the
eval
function to a variable or a property of an object.
obj.eval=...
new Function("eval")
Additionally, attempts to introduce new variables through an eval
will be blocked.
eval("var a=false;");
print( typeof a );
Finally, a long-standing (and very annoying) bug has been resolved: Cases where null or undefined is coerced into becoming the global object. Strict mode now prevents this from happening and throws an exception instead.
(function(){ ... }).call( null );
with(){}
statements are dead when strict mode is enabled - in fact it even appears as a syntax error.
Function arguments
There is an arguments
object that is available only within a function body. This might be useful in creating multiple class constructors or utility function which loop all
arguments
to perform some task e.g. sort.
function sort() {
alert(arguments.length);
arguments[1] = 5;
alert(arguments[1]);
}
sort(7,2,3);
sort("A","B","C", "D");
Closure
With nested JavaScript functions comes the ability of Closure i.e. retaining variables on stack even after function is returned. In a normal
circumstances, when a function is returned, its stack is destroyed as so is all variable. For a typical function as soon as it returns to calling environment, all its local variables and arguments become
eligible for garbage collection.
A closure occurs when a local variable of a function or function argument are kept alive even after the function has returned. Consider following function that returns an anonymous function. The anonymous inner function remembers what the value of
min
was when it was returned, even though inner function is called later in the code.
function volumeFactory(min) {
return function (value) {
return value + min;
}
}
var mode = "meeting";
var funcSetVolume;
if (mode === "meeting") {
funcSetVolume = volumeFactory(10);
} else {
funcSetVolume = volumeFactory(50);
}
alert(funcSetVolume(1));
A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created. I haven't mentioned but namespace, library,
and module discussed earlier are all examples of closure. A little more interesting example of
closure can be seen here.
JavaScript is an OO Language?
Developers are familiar with the notion of inheritance that a 'Derived class is a kind of Base class'. They know this from their education days and from computer science text books. So they are wired to think that class
Car
extends
Vehicle {}
and class Truck
extends
Vehicle {}
is a quick OO implementation of parent-child or is a kind of relationship with
Vehicle
as base class of Car
and Truck
.
When such developers try OO in JavaScript they realize that beside parent or child there are friends. The relationship between two friends is to ask for help from other friends if you don't know how to do it by yourself! So in a way, JavaScript prototype based chaining is telling that parent are actually friend of their child :) But this is just an
opinion.
Developers are good at thinking and modeling things in parent-child relationship and they do it for two simple reasons beside being taught. A) To better understand code and B) To reuse code. You have seen that JavaScript provide variety of ways to do both of these things, but in a bit different way. Once you get hold of the language, you will love it.
I have just skimmed the surface of what JavaScript has to offer. Hopefully we will see more of its capabilities in later parts of this series.
What's Next
Initially I thought I will cover JavaScript frameworks like Backbone, Angular, Knockout etc. in this part but it seems
adequate to leave it for our next discussion and keep this part dedicated to just JavaScript. We will the look into these popular frameworks and see what problems they solve or present.