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.