Wednesday, November 30, 2011

JS Automatic Semicolon Insertion

Semicolon; Why should I care?

"Javascript is the only language which people dare to use before learning"
- Crockford

Actually I, myself belong to that category of people whom crockford mentions :)
But trying to be out..

So, What's new today?
Just a informative writeup about ASI

ASI?
Ya, Javascript Automatic Semicolon Insertion

What is a legal statement in Javascript?
Following are some
var a=10;
a;
b++;
b+=1;
;;; // 3 Empty Statements
+a
var a = function() { };
{
  a
  b
  c    
};

Lets start
Try the following
var a=10;
function test() {
    var b;
    b = a;
    b+=1
}
console.log(a)

Even though I didn't insert any semicolon [Statement Terminator] in line 5 and 7, the JS engine never throws an syntax error.
Reason: ASI Construct

Rules to remember:
    ** ASI will insert one for you, if you specify a line terminator @ [no line terminator] mentioned
       in the grammar specification
    ** Whenever a statement misses a semicolon and if the statement following it makes sense along with the former.
       Then JS engine will not place a semicolon. Perhaps it parse them as a single statement
    ** If the statement is the last one inside a scope then you might consider missing semicolons. ASI will take care of

Where ASI will mislead?
There are 6 places
1 return
2 break
3 continue
4 Post ++
5 throw
6 Anywhere :P

Let's see the grammar for the first five
1. Return Statement             ->    return [no line terminator] (space expression)
2. Break Statement              ->    break [no line terminator] (space label)
3. Continue Statement         ->    continue [no line terminator] (space label)
4. Post Increment Statement ->    [operand] [no line terminator] ++
5. Throw Statements             ->    throw [no line terminator] [space expression]

Try it yourselves:
function test() { //Sample 1 Fails Silently
    var a =10;
    return 
    {
        avalue: a
    };
}
console.log(test().avalue);

function test() { //Sample 2 Throws Syntax Error
    var a =10, b = 10;
    return 
    {
        avalue: a,
        bvalue: b
    };
}
console.log(test().avalue);

Transformation:
function test() { //Sample 1 Fails Silently
    var a =10;
    return; --ASI rule 1-- Returns Undefined
    {
        avalue: a
    };
}
console.log(test().avalue);

function test() { //Sample 2 Throws Syntax Error
    var a =10, b = 10;
    return; // --ASI rule 1-- Returns Undefined 
    {    //Considered as a Block Statement            
        avalue: a,  //Considers avalue as label and 'a' as the statement under it
        bvalue: b   //Considers bvalue as label and 'b' as the statement under it
    };
}

According to ASI rule 1, there should not be any line terminator between return keyword and optional value.
If there is line terminator then JS parser inserts one ';' for you and makes your life difficult in case 2 and even more in case 1 [fails silently]

Try it yourselves:
These things likely won't happen but...
outer:for(j=0;j<3;j++) //Sample 1
{
    test:for(var i = 0;i < 3; i+=1) {
            if(i === 2) {
                break     // JS Engine Inserts a ; and 'outer' becomes a reference to outer loop
                    outer;
            }
           console.log(j);    
    }
}
console.log(i);

var a = -10;        //Sample 2
var b = 10        
+a;        // Rule 2
console.log(a+' '+b);

var a, b = 10, c = 10, d = 1, e = 3;  //Sample 3
a = b + c
(d + e).toString(2);    //Rule 2

var a = 10;    //Sample 4
a
++;

var b = 1; //Sample 5
++
b;

var a = 10, b = 10; //Sample 6
if(true)
{
    a++;
    b++        // Rule 3
}

Tidy programming always helps and will keep javascript good :)