Saturday, July 19, 2014

08 - Comparison and Type Conversion

There are two comparison operators in JavaScript. One is ==, and the other is ===. The difference of these two operators is about type conversion. If two values in different types are compared with ==, they will be converted into the same type first and then compared. There is no type conversion when values are compared with ===.

Type Conversion with ==
Let's discuss how types are converted when they are compared with == one by one.  The first type is Boolean values false and true. When one operand of the == operator is a Boolean value and the other operand is an integer, the Boolean value false is converted to 0, and true is converted 1. When one operand of the == operator is a Boolean value and the other operand is a string, the Boolean value false is converted to "0", and true is converted "1", as demonstrated below:

console.log(false == 0);   // true
console.log(true == 1);    // true
console.log(true == 3);    // false 

console.log(false == "0"); // true
console.log(true == "1");  // true

When one operand of the == operator is a string and the other operand is an integer, the string will be converted to an integer value. Therefore, "3" == 3 gets true, as shown below:

console.log("3" == 3);      // true  

When a string is compared with a Boolean value, both an empty string and "0" are converted to false, "1" converted to true. Any other strings can neither converted to true, nor false.

console.log("0" == false);      // true
console.log("" == false);       // true
console.log("1" == true);       // true
console.log("2" == true);       // false
console.log("2" == false);      // false

console.log("true" == true);    // false
console.log("false" == false);  // false

It becomes complex when a string is used as a condition in an if expression. In such cases, only an empty string is treated as false, and all others are treated as true:

var valueFunction = function(name, str) {
    if(str) {
        console.log(name, "is true");
    }
    else {
        console.log(name, "is false");
    }
}

valueFunction("Empty string", "");  // Empty string is false
valueFunction("0", "0");            // 0 is true
valueFunction("1", "1");            // 1 is true
valueFunction("2", "2");            // 2 is true

Two special values null and undefined are equal to each other when compared with the == operator, but they are not equal to any other values:

console.log(null == undefined);  // true 

console.log(null == undefined);  // true  

console.log(null == false);      // false
console.log(undefined == false); // false
console.log(null == 0);          // false
console.log(undefined == 0);     // false
console.log(null == '');         // false

Even null and undefined are not equal to the Boolean value false, they are treated as false when they are used as conditions in if expressions:

var valueFunction = function(name, value) {
    if(value) {
        console.log(name, "is true");
    }
    else {
        console.log(name, "is false");
    }
}

valueFunction("undefined", undefined); // undefined is false
valueFunction("null", null);           // null is false

The behavior of NaN is also quite special, which does not equal to any values, including NaN itself:

console.log(NaN == 0);     // false  
console.log(NaN == 'NaN'); // false  
console.log(NaN == NaN);   // false 

When an instance of a reference type is compare with a value type, the instance will be converted by invoking valueOf method:

var a = "hello world";
var b = new String("hello world");
console.log(a == b); // true

When two instances of reference types are compared, they equal to each other only when they are the identical instance:

var a = [1, 2, 3];
var b = [1, 2, 3];
console.log(a == b); // false

var c = a;
console.log(a == c); // true;

Equality with the == operator is reflective, but not transitive. That's to say, if A == B, B == A. However, if A == B and B == C, it's not necessary A == C. The following is an example:

console.log("0" == 0);     // true
console.log(0 == "");      // true
console.log("0" == "");    // false

No Type Conversion with ===
When two values are compare with the operator ===, they equal to each other only when they are in the same type and their value are identical. That's to say, no type conversion is involved in === comparison. When two values are not in the same type, they don't equal to each other when they are compared with the === operator. Let's take the following piece of code as an example:

console.log(false === 0);        // false
console.log(true === 1);         // false
console.log("3" === 3);          // false

var a = "hello world";
var b = new String("hello world");
console.log(a === b);            // false

console.log(null === undefined); // false

All pairs of values are not equal when compared with the operator ===, and two values in a pair have different types. As we discussed before, every pair of values are equal when compare with the operator ===.

Friday, July 11, 2014

07 - Arrays


Array Construction
Arrays can be created by invoking the constructor Array:
 
var array1 = new Array();
array1[0] = "a";
array1[1] = 1;
array1[2] = true;

console.log(array1); // ["a", 1, true]
 
Notice that elements in an array are not necessary to have the same type. Three elements in array1 in the code above are a string, number, Boolean value correspondingly.
 
There is also a more concise way to construct an array, as shown below:
 
var array2 = ["a", 1, true];

console.log(array2); // ["a", 1, true]
 
Elements are surrounded by a pair of square brackets during the array construction.
 
Iterating Array Elements
Some elements inside an array may be undefined. When an array with some undefined elements is traversed with different strategies, the results might be different. Let's take the following piece of code as an example:
 
var array = [0, 1];
array[4] = 4;

console.log("length:", array.length); // length: 5
for(var index = 0; index < array.length; ++index) {
    console.log(index + ":", array[index]);
}

// Output:
// 0: 0
// 1: 1
// 2: undefined
// 3: undefined
// 4: 4

for(index in array) {
    console.log(index + ":", array[index]);
}

// Output:
// 0: 0
// 1: 1
// 4: 4
// name: linear increasing

array.forEach(function(value, index) {
    console.log(index + ":", value);
});

// Output:
// 0: 0
// 1: 1
// 4: 4
 
Firstly, an array is initialized with two numbers 0 and 1. The element with index 4 is set as 4. Now array has 5 elements, but the two elements with index 2 and 3 are undefined yet. When the array is traversed in the first for loop, both defined and undefined elements are listed. However, when the array is traversed in the for…in loop, only defined elements are listed.

The difference between the for...in loop and the method forEach is that for...in loop also iterates the property of the array, but the forEach does not. A property name is added into the array, and it is printed out when the array is iterated with for...in loop, but not with the forEach method.
 
Basic Array Functionalities
There are many predefined array methods, which fulfill basic array functionalities. The code below shows some most frequently used methods:
 
var array1 = [1, 2];
var array2 = [4, 3, 2];
var array3 = [5, 6];

var array4 = array1.concat(array2, array3);
console.log(array4); // [1, 2, 4, 3, 2, 5, 6] 

var index1 = array4.indexOf(2);      
var index2 = array4.lastIndexOf(2);  
console.log(index1); // 1
console.log(index2); // 4

var str = array4.join("; ");
console.log(str); // 1; 2; 4; 3; 2; 5; 6

var array5 = array4.slice(2, 5);
console.log(array5); // [4, 3, 2]

array4.sort();
console.log(array4); // [1, 2, 2, 3, 4, 5, 6]
 
The method concat concatenates two array into a longer one. The methods indexOf and lastIndexOf get the first and last index of the given element out of an array respectively. The method join convert an array into a string with the given delimiter between elements. The method slice gets a substring out of a string. As the name suggests, the method sort is used to sort an array.
 
Sort
Sometimes the result of the method sort is surprising. The following is an example
 
var array = [4, 20, 10, 7];
array.sort();
console.log(array); // [10,20,4,7]
 
After the array with numbers 4, 20, 10, and 7 gets sorted, the result is unexpected. That is because the default criteria to sort in alphabetical order. If the desired behavior is to sort based in numeric order, we have to define our own callback function to compare elements in the array:
 
var array = [4, 20, 10, 7];

function compare(a, b) {
    return a - b;
}

array.sort(compare);
console.log(array); // 4,7, 10, 20
 
Arrays as Stacks
Stacks are not defined explicitly in JavaScript. Fortunately, arrays can be used as stacks, because the methods push and pop are defined on arrays:
 
var array = [1, 2];

array.push(3); // [1, 2, 3] 
console.log(array);

array.push(4); // [1, 2, 3, 4]
console.log(array);

console.log(array.pop()); // 4
console.log(array.pop()); // 3
console.log(array); // [1, 2] 
 
As expected, the method push appends an element into the end of an array, and the method pop removes the last element of an array.
 
Similarly, there are two more methods to manage elements at the beginning of an array. The method unshift inserts at the beginning of an array, and the method shift deletes the first element:
 
var array = [1, 2];

array.unshift(3);
console.log(array); // [3, 1, 2]

array.unshift(4);
console.log(array); // [4, 3, 1, 2]

console.log(array.shift()); // 4
console.log(array.shift()); // 3
console.log(array); // [1, 2]
 
Arrays as Queues
If we always insert elements into an array with the method push, and delete elements with the method shift, elements are in the first in first out order, as demonstrated below:
 
var queue = [1, 2];

queue.push(3); // [1, 2, 3] 
console.log(queue);

queue.push(4); // [1, 2, 3, 4]
console.log(queue);

console.log(queue.shift()); // 1
console.log(queue.shift()); // 2
console.log(queue); // [3, 4] 
 
Delete, Replace, and Insert Elements
 
There is a more powerful method to insert, delete, and to replace elements at any places inside an array, which is named as splice. Where there only two parameters are passed into the method splice, the method is used to delete elements from an array:
 
var array1 = [1, 2, 3, 4, 5, 6, 7];
array1.splice(3, 2);
console.log(array1); // [1, 2, 3, 6, 7]

var array2 = [1, 2, 3, 4, 5, 6, 7];
array2.splice(-3, 2);
console.log(array2); // [1, 2, 3, 4, 7]
 
The first parameter stands for the starting index of elements to be deleted. If it is a negative number, the index starts from the right side of an array. For example, the starting index 3 in the array [1, 2, 3, 4, 5, 6, 7] is in value 4, and the starting index -3 in the array is in 5.
 
The second parameter is for the number of elements to be deleted. When two elements starting from the index 3 are delete from the array [1, 2, 3, 4, 5, 6, 7], it becomes [1, 2, 3, 6, 7]. And it becomes [1, 2, 3, 4, 7] when two elements are deleted starting from the index -3.
 
If there are more than two parameters are passed into the method splice, it is used two replace elements. For instance:
 
var array1 = [1, 2, 3, 4, 5];
array1.splice(2, 2, -1);
console.log(array1); // [1, 2, -1, 5]

var array2 = [1, 2, 3, 4, 5];
array2.splice(2, 2, -1, -2);
console.log(array2); // [1, 2, -1, -2, 5]

var array3 = [1, 2, 3, 4, 5];
array3.splice(2, 2, -1, -2, -3);
console.log(array3); // [1, 2, -1, -2, -3, 5]
 
In the code above, two elements starting from the index 2 (elements with value 3 and 4) are replaced with elements in the parameter list starting from the index 2 (negative parameters in the examples above).
 
If we are going to insert elements into an array, the second parameter of the method splice should be 0. The following is an example to insert elements:
 
var array = [1, 2, 3, 4, 5];
array.splice(2, 0, -1, -2);
console.log(array); // [1, 2, -1, -2, 3, 4, 5] 
 
In the code above, two negative numbers -1 and -2 are inserted into the array [1, 2, 3, 4, 5], starting from the index 2.
 

Tuesday, July 8, 2014

06 - Strings

In JavaScript, characters are also strings with length 1, as demonstrated below:

var character = 'a';
var string = "abcdefg";

console.log(typeof(character)); // string
console.log(typeof(string)); // string

The code above also shows that a string can be inside both single and double quotes. Additionally, strings inside single quotes can have double quotes, and strings inside double quotes can have single quotes vice versa, as the strings str1 and str2 in the following piece of code. If you would like to use double quotes in a string surrounded by double quotes, an escape character "\" should be inserted before the double quotes in the string. Strings with single quotes are similar. Please take the strings str3 and str4 in the code below as examples:

var str1 = "I can't wait to learn JavaScript.";
var str2 = 'He said: "JavaScript is so good".';

console.log(str1); // I can't wait to learn JavaScript.
console.log(str2); // He said: "JavaScript is so good".

var str3 = 'I can\'t wait to learn JavaScript.';
var str4 = "He said: \"JavaScript is so good\".";

console.log(str1 == str3); // true
console.log(str2 == str4); // true

Strings can be split into multiple lines, when a ending character "\" at the each line, as show below:

var str = "<div> \
Learning JavaScript \
</div>";

console.log(str); // <div> Learning JavaScript </div>

Strings in JavaScript are immutable. We could get a character from a string based on its index, but can modify it:

var str = "hello";
console.log(str[1]); // e

str[1] = 'a';
console.log(str[1]); // e

The character with index 1 in the string "hello" is "e". If we try to modify it to "a", the string keeps unchanged.

The type String has many methods to modify strings. The modified contents is accessible in the returned values, but the original strings are not modified, as demonstrated in the following piece of code:

var str1 = "hello";
var str2 = str1.replace("h", "H");

console.log(str1); // hello
console.log(str2); // Hello

The original value of the string str1 is "hello". When we try to replace the "h" with "H" in the string, str1 keeps unchanged, and the modified contents "Hello" is in the returned value which is referenced by str2.

Saturday, July 5, 2014

05 - Numbers

There are no differences between integers and floats in JavaScript. All numeric values are represented in 64-bit floats.

var intValue = 2;
var floatValue = 3.6;

console.log(typeof(intValue)); // number
console.log(typeof(floatValue)); // number

As demonstrated in the code above, both the integer value 2 and the float value 3.6 are in the type of number.

Numbers in JavaScript can be large as ±1.7976931348623157×10^308 (Number.MAX_VALUE), and as little as ±5 × 10^324 (Number.MIN_VALUE). Since number values are in a very wide range, it is not necessary to handle the big-data issue specially in most cases.

var bigNumber = Math.pow(2, 100);
console.log(bigNumber); // 1.2676506002282294e+30

It is quite easy to calculate big values without considering the overflow issue. As shown in the code above, it calculates 2 to the power of 100 in a line of code.

When overflow occurs, there are no errors thrown. The code with overflow executes normally, and returns predefined values. The following is an example:

try
{
    var tooBig = Math.pow(10, 400);
    console.log("Finish without error. The returned value is:", tooBig);
}
catch(e)
{
    console.log("A error was caught.");
}

// Output:
// Finish without error. The returned value is: Infinity

When overflow occurs, the predefined special value Infinity is returned if the result is positive. Similarly, it returns -Infinity when overflow occurs with a negative result.

Moreover, no errors will be thrown when a number is divided by 0, as shown below:

var result1 = 1 / 0;
var result2 = -1 / 0;

console.log(result1); // Infinity
console.log(result2); // -Infinity

Another special value NaN (No a Number) is returned when 0 is divided by 0, or non-numeric values are calculated:

var result1 = 0 / 0;
console.log(result1); // NaN

var result2 = 5 / "hello";
console.log(result2); // NaN

04 - Functions as Objects

Functions in JavaScript are objects, so functions can be assigned to variables, passed to other functions as parameters, returned from other functions. Additionally, functions have their own properties.

Functions Assigned to Variables
Functions in JavaScript can be defined and assigned to variables, and then invoked via the variables, as shown in the following example:

var add = function(a, b) 
    return a + b;
};

console.log(typeof(add)); // function
console.log(add(1, 2)); // 3
An anonymous is defined, and then is assigned to variable name add.  Now the variable name add is referenced to the function, and can be invoked as the function.

Functions as Parameters
Functions can also be passed to other function as parameters, as shown below:

function calculate(operator, a, b) {
    return operator(a, b);
};

function add(a, b) {
    return a + b;
};

console.log(calculate(add, 2, 5)); // 7
console.log(calculate(multiply, 2, 5)); // 10

The first parameter of the function calculate is a function. When the function add is passed as the parameter, it adds two numbers. Otherwise it multiplies two numbers if the function multiply is passed.

When a function is passed as a parameter to another function, it is called a callback function. There are many APIs take callback functions as parameters, which provide much flexibilities to use the APIs. For example, we could pass a callback function to add up the elements of an array to the forEach method of an array, as shown below:

function sum(nums) {
    var total = 0;
    nums.forEach(function(num) {
        total += num;
    });
 
    return total;
};

var nums = [1, 2, 3, 4];
console.log(sum(nums)); // 10 

An anonymous function, which adds an element of the array to the variable total, is passed to the method forEach. When the method forEach is invoked with the callback function, the total sum of all elements in the array is added up.

Functions as Return Values
Similar to other kinds of objects, functions can also be returned from functions. As shown in the following piece of code, the function addFunc returns a function, which adds two numbers:

function addFunc(a, b) {
    return function() {
        return a + b;
    }
};

var func = addFunc(3, 5);
console.log(typeof(func)) // function
console.log(func()); // 8 

The variable func references to the returned function of addFunc, so the type of func is a function. Additionally, the function can be invoked as normal functions.

Functions with Properties
Objects have their properties. So Functions. We add a property count to the function test below:

test.count = 0;
function test() {
    test.count++;
    console.log(test.count);
}

test(); // 1
test(); // 2
test(); // 3

When the function test is invoked, the property count increases. We could implement the same functionalities with another global variable. However, the property count has some advantages, because it will not conflict with other variables. That's to say, it is OK for us to define a global count, since it will not conflict with the property test.count.

Similarly, functions can be viewed as arrays, and they can have their own elements.  As we know, it is quite time-consuming to  calculate the Fibonacci sequence recursively, if without caching. We may cache the intermediate results in the function, as listed below:

function fibonacci(n) {
    if(fibonacci[n - 2] === undefined) {
        fibonacci[n - 2] = fibonacci(n - 2);
    }

    if(fibonacci[n - 1] === undefined) {
        fibonacci[n - 1] = fibonacci(n - 1);
    }

    return fibonacci[n - 1] + fibonacci[n - 2];
}

fibonacci[0] = 0;
fibonacci[1] = 1;

console.log(fibonacci(10)); // 55
console.log(fibonacci(50)); // 12586269025

The function Fibonacci is also an instance of array. When a value of the Fibonacci sequence is calculated, it is cached into the array. It is more efficient to calculate next values based on the cached values.

Wednesday, July 2, 2014

03 - IIFE and Modules


Current there are neither classes nor namespaces in JavaScript. However, we could  modularize JavaScript code with the Immediately Invoked Function Expression (IIFE).

Firstly, let's define a variable and two functions as the following, without modularization:

var count = 0;

function print()
    console.log(count);
}

function increase()
{
    count++;
}

In the code above, the function names print and increase are available globally, and it is highly possible for them to conflict with other variable or function names. Therefore, it is necessary to protect them inside a module.

The first modularization solution is to utilize objects. The variable and functions become members of an object, as listed below:

var module = new Object({
    count: 0,

    print: function() {
        console.log(this.count);
    },

    increase: function() {
        this.count++;
    }
});

The problem with this solution is that the member variable count is accessible outside the object, which usually is not our purpose because we would like to hide data but just expose interfaces. The following code shows how to modify the member variable count:

module.count = 5;
module.increase();
module.print(); // 6

The member variable count is initialized as 0 firstly. It is increased to 6 then, so the output is 6.

A better solution to modularize is to utilize IIFE. The code above can be refined into the following section of code:

var module = function() {
    var count = 0;

    var print = function() {
        console.log(count);
    };

    var increase = function() {
        count++;
    };

    return {
        print: print,
        increase: increase
    };
}();

In the code above, count is a local variable inside the anonymous function, and it is inaccessible outside the function. Therefore, only the interfaces are exposed but the details are hidden.

We could demonstrate that the variable count is inaccessible with the following piece of code:

module.count = 5; 
module.increase();
module.print(); // 1

Even though we try to set module.count as 5, it actually inserts a property count with value 5 into the object returned by the anonymous function, but the variable count inside the function keeps unchanged. After the variable count increases, it becomes 1, so the output is 1, instead of 6.

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.