Saturday, January 11, 2014

02 - Parameters and Arguments

The Number of Parameters and Arguments

The term parameter is used to describe the names for values that are expected to be supplied. The term argument is used for the values provided for each parameter. In many languages, the number of parameters and the number of arguments should be same. However, JavaScript behaves in a different way. The number of parameters declared in a JavaScript function signature can be different from the number of arguments passed to the function when it is invoked.

If some arguments are not passed into a function when it is invoked, the values of the missing  arguments are undefined. Let's take a look at the following sample code:
function greeting(name, greetingWord) {
    if (greetingWord === undefined) {
        greetingWord = "Hi";
    }
    
    console.log(greetingWord + ", " + name);
}

greeting("Harry", "Hello"); // Hello, Harry 
greeting("Peter"); // Hi, Peter
A function greeting with two parameters is declared firstly, then it is invoked twice. When two arguments are passed, they are concatenated and logged into the console. It's more interesting when there is only one argument "Peter" passed into the function. Arguments match parameters from left to right. If some parameters are not matched to passed arguments, they remain undefined. Therefore, the parameter name becomes "Peter", and greetingWord becomes undefined. Inside the function, a default value "Hi" is assgined when greetingWord is undefined, so "Hi, Peter" is logged to the console eventually.

When the number of arguments passed into the function when it is invoked is more than the number of parameter in the function declaration, the unmatched arguments will be discarded.
For exapmle, a function add with two parameters is declared in the following sample code, and then three arguments are passed when it is invoked:
function add(num1, num2) {
    return num1 + num2;
}

console.log(add(1, 2, 3)); // 3
Again, arguments match parameters from left to right. If some arguments don't match any parameters, they are not in use in the function. Therefore, num1 becomes 1, and num2 becomes 2 in the code above, and the returned sum is 3.

Arguments Property of Functions

Each function is an object in JavaScript, so a function can have its own properties. One of the interesting properties is arguments, which contains all the arguments passed into the function when it's invoked. Even more arguments are passed into function, all of the arguments  are accessible via the arguments property.

The following sample code shows how to use the arguments property:
function add() {
    var sum = 0;
    for(var i = 0; i < arguments.length; ++i) {
        sum += arguments[i];
    }
    
    return sum;
}

console.log(add(1, 2, 3)); // 6
A function add is declared with no explicit parameters. It is then invoked with three arguments, which are 1, 2, and 3 respectively. These argument are not acutally discarded, but remain accessible via the arguments property. The arguments property is an object, which looks like an array, and have its own property length and elements are indexed beginning from 0. Therefore, the returned sum is 6, which equals to 1+2+3.

No Overloading

Many programming languages have the overloading mechanism, which allows many functions are defined with same name but difference number of parameters. However, there is no overloading in JavaScript, because a function can be invoked with difference number of arguments. If multiple functions with the same name but different number of parameters are defined, the last function will overwrite the  preceding ones, as shown in the sample code below:
function add(num1, num2, num3) {
    return num1 + num2 + num3;
}

function add(num1, num2) {
    return num1 + num2;
}

console.log(add(1, 2, 3)); // 3
In the code above, a function named add is defined with three parameters are defined at first, and then another function also named add is defined with two parameters. The latter function overwrite the former one. When the function add is invoked with three arguments, only two arguments passed into the second function and the last argument 3 is not in use, so the returned value is 3 which is 1 plus 2.

Property arguments.callee

The arguments property of functions is an object, and it has its own properties. Besides arguments.length, another interesting property is arguments.callee, which points to the container function of arguments.

The property arguments.callee is widely used in recursive function. Recursive functions call themselves, usually through function names. There are anonymous functions in JavaScript, which don't have names. How to define recursive functions when they are anonymous? A solution is to name them explicitly, and the other one is to utilize arguments.callee. Let's have a look at the following sample code:
var threeToPowerOfTen = (function (exponent) {
    if (exponent === 1) {
        return 3;
    }
    return 3 * arguments.callee(exponent - 1);
})(10);

console.log(threeToPowerOfTen); // 59049 
In order to get the value of 3 to power of 10, an anonymous function is defined which is also recursive. Since an anonymous function doesn't have a name, it can't be recursively invoked via its name. In the code above, the property arguments.caller is utilized to invoke the anonymous function. That's to say, the property arguments.callee makes it possible to define recursive anonymous functions.

The property arguments.callee has its own value even a recursive function has a name. When a function is recursively invoked via its name, the recursive functionality couples with its name. The function name is just a variable name, and it can be assigned to other functions, or even other object. When the function is assign another meaning, the recursive functionality breaks. For example:
function power(base, exponent) {
    if (exponent == 1) {
        return base
    }

    return base * power(base, exponent - 1);
}

var realPower = power;

power = "More power, more powerful";

console.log(realPower(2, 3));

// Result:
// TypeError: string is not a function
A recursive function power is defined in the code above. It's recursively invoked via its name power inside the function. Following the function declaration, the function is assigned to another variable name realPower, and then power is assigned to a string. Finally the function is invoked which has can be referenced with the name realPower, but its functionality breaks. Inside the function, the it recursively invokes itself via the name power, but power now is not a function anymore. Therefore, an error is thrown complaining that a string is not a function.

In order to decouple the recursive functionality and the function name, the function can be replaced with argument.callee which also points to the function. Therefore, the recursive function can be revised into the following code:
function power(base, exponent) {
    if (exponent == 1) {
        return base
    }

    return base * arguments.callee(base, exponent - 1);
}

var realPower = power;

power = "More power, more powerful";

console.log(realPower(2, 3)); // 8
In the revised code, the function recursively call itself via arguments.call. Therefore, even when the name power is assigned to a string, the recursive functionality remains.

Wednesday, January 8, 2014

01 - Define Functions

Two Ways to Define Functions

There are two different ways to define functions in JavaScript. The first way is to name explicitly, as shown below:
 function helloWorld() {
    console.log("Hello world.");
}

helloWorld();

// Output:
// Hello world.
In the code above, a function renamed helloWorld is defined. When it's invoked, a line of "Hello world." is logged into the console.

We can also define an anonymous function. Since a function in JavaScript is also an object, the anonymous function can be assigned to a variable name. The function then can be invoked via the variable name. The following is an example:
var helloWorld = function () {
    console.log("Hello world.");
}

helloWorld();

// Output:
// Hello world.
There is a striking differences between the two ways to define functions. An explicitly named function can be invoked ahead of its definition, as shown in the example below:
helloWorld();

function helloWorld() {
    console.log("Hello world.");
}

// Output:
// Hello world.

The function helloWorld is defined when the code is loaded, so it can be invoked even though the function call appears ahead of its definition.

The same scenario does not work for anonymous functions. For example, an error will be thrown in the sample below:
helloWorld(); // TypeError: undefined is not a function

var helloWorld = function () {
    console.log("Hello world.");
}
The anonymous function gets defined when the code is loaded, but the variable helloWorld won't be defined (be assigned to the anonymous function) when the line of code get executed. Therefore, when we try to call the function via the variable name helloWorld, the value of the variable is still undefined.

Immediately Invoked Function Expression

A function can be invoked immediately when it's defined. The following is an example:
(function() {
    console.log("Hello world.");
})();

// Output:
// Hello world.
In the code above, an anonymous function is defined. There is a pair of parentheses after the function definition, which indicate that the function is invoked immediately. 

An immediately invoked function expression is a widely used design pattern in JavaScript. The singleton pattern can be implemented based on an immediately invoked function. The following is an exapmle:
var Singleton = (function() {
    function SingletonClass() {
    }
   
    var instance = new SingletonClass();
   
    return {
        getInstance: function() {
            return instance;
        }
    };
})();
var instance1 = Singleton.getInstance(); var instance2 = Singleton.getInstance(); console.log(instance1 === instance2);
// Output: // ture
Inside the outer anonymous function, a constructor SingletonClass is defined, and an instance of SingletonClass is created. In the returned instance of the anonymous function, an instance of SingletonClass is returned in the member function getInstance. Notice that the constructor SingletonClass is defined inside an anonymous function, no one else can invoke the constructor to create new instances.

Outside the anonymous function, the returned instance is assigned to a variable name Singleton, then the unique instance of SingletonClass can be accessible via the function Singleton.getInstance. If the function Singleton.getInstance is invoked for multiple times, only one instance of SingletonClass will be returned, so instance1 and instance2 are identical. It demonstrates that requirements of the singleton pattern have been fulfilled.

Immediately invoked functions can also be utilized to keep private variables inaccessible. More details will be discussed in other posts later. 

Thursday, January 2, 2014

JavaScript in Practice (2) - Fireworks Animation

Here we are going to animate fireworks, with JavaScript and HTML5. The animation result looks like the image blow:

If you are interested to the dynamic animation, please go the webpage http://jsfiddle.net/zhedahht/rKsyE/. Additionally, the source code is shared at https://github.com/zhedahht/JsInPractice/tree/master/Fireworks.

Now let's dive into the source code of the fireworks. Fireworks are defined as lots of moving particles. Particles are created with the following JavaScript constructor:

function Particle(pos, speed, resistance, gravity, size) {
    var curPos = {
        x: pos.x,
        y: pos.y
    };
    var curSpeed = speed;
  
    this.render = function(context, color) {
        context.fillStyle = color;
        context.beginPath();
        context.arc(curPos.x, curPos.y, size, 0, Math.PI * 2, true);
        context.closePath();
        context.fill();
    }
  
    this.update = function() {
        curSpeed.x = curSpeed.x * resistance + gravity.x;
        curSpeed.y = curSpeed.y * resistance + gravity.y;
      
        curPos.x += curSpeed.x;
        curPos.y += curSpeed.y;
    }
}

Each particle is response to render itself as a dot on a canvas, and to update its position. 

A group of particles are shot from time to time, which share some common properties such as color and the initial position before explosion. A group of particles in a single shot are defined as:

function ParticleGroup(pos, canvasSize, numberOfParticles) {
    var shotHeight = randomInRange(canvasSize.height * 0.50,
                                   canvasSize.height * 0.75);
    var life = 100;
    var age = 0;
    var particles = initParticles(pos, canvasSize);
    var color = pickColor();


    this.render = function(context) {
        var strColor = color.toString();
        
        particles.forEach(function(particle) {
            particle.render(context, strColor);
        });
    }
    
    this.update = function() {
        age++;


        updateColor();
        
        particles.forEach(function(particle) {
            particle.update();
        });
    }
    
    this.isDead = function() {
        return age >= life;
    }
    
    function initParticles(pos, canvasSize) {
        var particles = [];


        var particlePos = {
            x: pos.x,
            y: pos.y - shotHeight
        }


        var resistance = 0.985;
        var gravity = {
            x: 0,
            y: 0.005
        }
        var size = 2;
        
        var maxSpeed = randomInRange(2.4, 3.2);


        for(var i = 0; i < numberOfParticles; ++i) {
            var angle = randomInRange(0, Math.PI * 2);
            var linearSpeed = randomInRange(0, maxSpeed);
            var speed = {
                x: linearSpeed * Math.cos(angle),
                y: linearSpeed * Math.sin(angle),
            }
            
            var particle = new Particle(particlePos, speed, resistance,
                                        gravity, size);
            particles.push(particle);
        }
        
        return particles;
    }
    
    function updateColor() {
        var alpha = 1.0;
        var oldness = age / life;
        if (oldness > 0.90) {
            alpha = 10 * (1 - oldness);
            color.setAlpha(alpha);
        }
    }
}

The following constructor is to create fireworks, which is response to shot new group of particles from time to time, and remove groups when they get too old:

function Firework(pos, canvasSize, numberOfParticles) {
    var shots = [];
    this.render = function(context) {
        shots.forEach(function(shot) {
            shot.render(context);
        });
    }
    
    this.update = function() {
        removeDeadShots();
        
        shots.forEach(function(shot) {
            shot.update();
        });
    }
    
    this.shot = function() {
        var newShot = new ParticleGroup(pos, canvasSize, numberOfParticles);
        shots.push(newShot);
    }
    
    function removeDeadShots() {
        for(var i = 0; i < shots.length; ++i) {
            shot = shots[i];
            if (shot.isDead()) {
                shots.splice(i, 1);
            }
        }
    }
}

Usually there are many fireworks shooting at the same time. The following constructor create a group of fireworks:

 function FireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {
    var fireworkGroupElement = document.getElementById(canvasId);
    var context = fireworkGroupElement.getContext("2d");
    
    var width = fireworkGroupElement.clientWidth;
    var height = fireworkGroupElement.clientHeight;
    
    var fireworks = initFireworkGroup(width, height);
    
    this.getFireworks = function() {
        return fireworks;<
    }

    this.render = function() {         context.fillStyle = "#010212";         context.fillRect(0, 0, width, height)                  fireworks.forEach(function(firework) {            firework.render(context);         });     }          this.update = function() {         fireworks.forEach(function(firework) {            firework.update();         });     }          this.shot = function() {         fireworks.forEach(function(firework) {            firework.shot();         });     }
    function initFireworkGroup(width, height) {         var fireworks = [];         for(var i = 0; i < numberOfFireworks; ++i) {             var pos = {                 x: Math.round((width / numberOfFireworks) * (i + 0.5)),                 y: height * 0.95             };             var canvasSize = {                 width: width,                 height: height             };
            fireworks[i] = new Firework(pos, canvasSize, numberOfParticles);             }                  return fireworks;     } }

With the code above, we can create fireworks on a canvas when the document is loaded:

$(document).ready(function () {
    makeFireworkGroup("canvasForfireworks", 3, 300);
});

function makeFireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {     function shotFireworkGroup(fireworkGroup) {         var fireworks = fireworkGroup.getFireworks();         fireworks.forEach(function(firework) {             shotFirework(firework);         });     }          function shotFirework(firework) {         firework.shot();                  var wait = randomInRange(1200, 1600);         setTimeout(shotFirework, wait, firework);     }          function renderAndUpdate(fireworks) {         return function() {             fireworks.render();             fireworks.update();         };     }
    var fireworks = new FireworkGroup(canvasId,                                       numberOfFireworks,                                       numberOfParticles);     shotFireworkGroup(fireworks);          var renderAndUpdateFunc = renderAndUpdate(fireworks)     setInterval(renderAndUpdateFunc, 15); }

In the code above, 300 particles are shot in about every 1.5 seconds, and the canvas will be updated in every 15 milliseconds. 

If you find that there are more and more particles on your canvas, it means there are too many particles. You may decrease the number of particles in each shooting.

Finally, some utility functions are needed for randomness and particle colors, as listed below:

function randomInRange(min, max) {
    return Math.random() * (max - min) + min;
}

var pickColor = (function() {     var colors = [         new Color(0x00, 0xFF, 0xFF), // Aqua         new Color(0x8A, 0x2B, 0xE2), // BlueViolet         new Color(0xDC, 0x14, 0x3C), // Crimson         new Color(0xFF, 0x14, 0x93), // DeepPink         new Color(0x22, 0x8B, 0x22), // ForestGreen         new Color(0xAD, 0xFF, 0x2F), // GreenYello         new Color(0xFF, 0x69, 0xB4), // HotPink         new Color(0xCD, 0x5C, 0x5C), // IndianRed         new Color(0xF0, 0xE6, 0x8C), // Khaki         new Color(0x7C, 0xFC, 0x00), // LawGreen         new Color(0x00, 0xFA, 0x9A), // MediumSrpingGreen         new Color(0xFF, 0xA5, 0x00), // Orange         new Color(0x80, 0x00, 0x00), // Purple         new Color(0xFF, 0x00, 0x00), // Red         new Color(0x87, 0xCE, 0xEB), // SkyBlue         new Color(0xFF, 0x63, 0x47), // Tomato         new Color(0xEE, 0x82, 0xEE), // Violet         new Color(0xF5, 0xDE, 0xB3), // Wheat         new Color(0xFF, 0xFF, 0x00)  // Yellow               ];          return function() {         var index = Math.round(randomInRange(0, colors.length - 1));         return colors[index].clone();     } })();
function Color(red, green, blue, alpha) {     var r = red,         g = green,         b = blue,         a = alpha;              this.toString = function() {         if (a === undefined) {             return "rgb(" + r + "," + g + "," + b + ")";         }                  return "rgba(" + r + "," + g + "," + b + "," + a + ")";     }          this.setAlpha = function(newAlpha) {         a = newAlpha;     }          this.clone = function() {         return new Color(r, g, b, a);     } }