JavaScript provides two categories of variable types: primitive and object types. The primitive type contains numbers, strings, boolean, null, NaN, and undefined. All of the primitive types are immutable; thus, once we define them, we cannot change their value. The second category contains object types; an object is essentially a collection of property/value pairs and is mutable. As an example, JavaScript arrays are objects and hence, belongs to the category of object type variables.
The first primitive JavaScript type is integer type. For this type of variables, JavaScript uses a 64 bit storage space for number variables. In fact, it provides one common type to store both integers and floats (fractional numbers). Thus, both integer and float require the same storage space.
The next primitive JavaScript type is string type; a string type is simply a finite ordered sequence of 16-bit characters; these characters can be letters, numbers (from 0 to 9), white-space etc. As an example, storing the JavaScript "Magic" would require a series of five 16-bit spaces, or a total of 80 bits of storage.
A JavaScript string is essentially a read-only array of 16-bit sized characters; we briefly talk about arrays towards the end of this page. The array is read-only since JavaScript strings are immutable. JavaScript stores the string length to mark the end of the string.
The remaining four primitive types are simpler. The boolean type essentially contains two values: true and false. The null type means a lack of value for a variable. The undefined type is similar and means that even though a variable is defined, it has not been assigned any value. The NaN type represents all those values that are not numbers. One unique thing about NaN is that it is the only type that does not equal itself; thus (NaN != NaN) evaluates to true.
Due to JavaScript's flexibility, we do not need to specify the type of a variable before using it. JavaScript is not a strongly-typed programming language and hence, a variable is not associative with a storage type for its entire lifetime. Depending upon the value assigned to a variable, JavaScript automatically determines the type of a variable.
When in doubt, we can use JavaScript's typeof operator to confirm the storage type of a variable. If its value is an integer or float, then the typeof would return number, if the value is a string, then typeof would return string, and so on.
Next, let us put typeof operator to test and see the output for the primitive type variables. Here is an example:
<!doctype html> <html> <div id="idDiv"> </div> <script type="text/javascript"> var varNum1 = 10; // varNum1 is a number. var varNum2 = 10.50; // varNum2 is a number. var varString = "Magic Spell"; // varString is a string. var varUndef; // varUndef is undefined since we assign nothing to it. var varNull= null; // varNull is of type null. // Get a handle of the div element. var elem = document.getElementById("idDiv"); elem.innerHTML += "Type of varNum1 is: " + (typeof varNum1) + " <br>"; elem.innerHTML += "Type of varNum2 is: " + (typeof varNum2) + " <br>"; elem.innerHTML += "Type of varString is: " + (typeof varString) + " <br>"; elem.innerHTML += "Type of varUndef is: " + (typeof varUndef) + " <br>"; elem.innerHTML += "Type of varNull is: " + (typeof varNull) + " <br>"; // Now, assign a number to varString and its type would change. var varString = 100; elem.innerHTML += "The new type of varString is: " + (typeof varString) + " <br>"; </script> </html>
The output of the above program is provided below. As expected, varNum1 with value "10" is a number (integer) type. Likewise, even though varNum2 with a value "10.50" is a float type, JavaScript uses the same number type to store it as well. Variable varString is a string since it holds text. The next variable, varUndef is declared but since we do not assign any value to it, JavaScript does not know the type of this variable. Hence, JavaScript classifies varUndef as undefined since the value assigned to varUndef is yet to be defined.
The output confirms that the type of a variable is dependent upon the value it stores and its type can change over its lifetime. For example, when varString stores "Magic Spell", its type is string. However, when we change its value to a number (100), its type automatically changes to number type.
Lastly, JavaScript provides several type-related built-in functions: parseInt(), parseFloat(), isNaN(), isFinite() etc. parseInt() returns an integer value when we pass an integer as a string. parseFloat() returns a float value when we pass a float as a string. isNaN() verifies if a number is NaN and returns true if it is NaN. isFinite() verifies if a number is finite and returns true for all numbers except Infinity. Here are some examples:
parseInt("101"); // returns 101 (a number). parseInt("Spell"); // returns NaN since "Spell" cannot be parsed as an integer. parseFloat("3.1414"); // returns 3.1414 (a number). parseFloat("Spell"); // returns NaN since "Spell" cannot be parsed as a float. isNaN(280); // returns false since 280 is not NaN. isNaN(NaN); // returns true. isFinite(Infinity); // returns false. isFinite(680); // returns true.
Besides primitive types, JavaScript supports yet another important variable type: JavaScript objects. Objects are an important JavaScript construct -- virtually all non-primitive types like arrays, functions, Dates, Regular Expressions are objects.
Object types are containers that hold property/value pairs and as opposed to primitive types, they are mutable. In JavaScript, anything that is not a primitive type, is an object. Thus, functions, arrays, regular expressions, Date etc are all objects.
Objects are containers that hold zero or more property/value pairs and the set of these pairs are enclosed within braces. Each object property should be separated from its value using a colon (":"). Thus, if were to define an object, objX, holding a property, propY with a value of 237, then we can do that as: "var objX = {propY: 237};".
In terms of property values, properties can be kept into categories: data and methods. When property value is data, then it stores non-functional values like numbers, strings, boolean, other objects etc. When property value is method, then it stores a function. These object functions or methods allow us to provide application logic that can specifically deal with object data. This ability provides a useful and clean way to segregate application data from application logic.
JavaScript provides two methods for accessing properties of an object. The first method uses the "." (single dot) operator; thus, to access propY of objX, we can use "objX.propY". The second method treats an object as an associative array and passes the property as a key to the array: objX["propY"].
Unlike primitive types, objects are mutable. This means that we can update value of properties as and when needed. Furthermore, we can even add or delete properties on the fly. To update the value of an existing property, we can simply reassign a new value to it: "objX.propY = 238;" To add a new property, we can assign something to the new property, as if it already existed and we are merely modifying it. Thus, "objX.propZ = 280;" would add a new property, propZ to objX. Lastly, to delete an existing property, we can use the delete operator. Thus, "delete objX.propZ; would delete the propZ property.
For objects, the typeof operator returns the "object" string.
When we define a JavaScript variable, the variable represents a single unit of data. However, sometimes, an application may need to store a series of data and its logic may need to perform a task on the entire series of data. For such use-cases, JavaScript provides a new type of objects: JavaScript arrays.
arraySimple[1] arraySimple[N-1] | | | | V V ----------------------------------------------------------------- | 1000 | "Dragon" | 1001.50 | undefined | .. | "Young Wizard" | ----------------------------------------------------------------- ^ | | arraySimple[0] Figure: A JavaScript Array holding N elements
The above figure shows an array, arraySimple, with N elements; in JavaScript, array elements do not necessarily have to be of the same type. Each array element is identified by an index. The first element has an index of 0, the second element has the next index of 1, and the last element has an index of N-1. Using these array indexes, we can easily navigate the array. Even though JavaScript uses 64-bit storage for numbers, it still uses a 32-bit index for array indexes and thus, the largest possible index for an array element can be (2^32 - 1).
To define the above array, we can use "var arraySimple = [1000, "Dragon", "Young Wizard"];". Further, to access individual elements, we can use the respective index of that element. Thus, to refer to the second element in the above array, we can simply use "arraySimple[1]".
Since JavaScript arrays as objects, the typeof operator returns the type of arrays as "object".
An important consideration for JavaScript variables is their scope. JavaScript offers two levels of variable scope: global and function-level. Variables that are not defined as part of any functions are global variables. Function-level variables are those variables that are defined within a function. These variables are visible only inside the function and accessing these function-level variables outside of their functions would simply be an error. For a variable to be local to a function, we need to use the keyword "var"; if we fail to do that, then the variable would automatically acquire a global scope!
Since it is possible that a programmer can name a global variable same as that of an existing one, it is a good idea to reduce the number of global variables to as minimum as possible. Also, since the default scope of a variable is global (as noted above, if we fail to add "var" to a variable inside a function, then the variable becomes global), this automatically leads to addition of a lot more variables in the global scope and thus, increases the chances of collisions. A lot of programmers generally regard global variables as evil, unless indicated otherwise!
Note that JavaScript does not provide a block-level scoping. As an example, in C, anything that sits within a curly braces (between "{" and "}") can have its own scope. However, with JavaScript, we have no such block-level scope.
A particularly interesting behavior of variable scoping is what is known as variable "hoisting". With "hoisting", we can access a variable even before it has been declared. However, we cannot access its value, since it has been assigned yet! For functions, this means that we can invoke a function before it has been defined! Nevertheless, there is an one important difference: unlike variable "hoisting", we still get to run the function normally as if the function were already defined.
Here is a simple example that highlights variable "hoisting". Even though we are able to access the variable, q, its value is still "undefined". To avoid such behavior, it is a good practice to define variables at the top of their scope (whether global or in a function).
<!doctype html> <html> <div id="idDiv"> </div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Access a variable before defining it. elem.innerHTML += varTemp; // this will print: "undefined". // Define the variable. var varTemp = 100; // Access the variable after defining it. elem.innerHTML += varTemp; // this will print: "100". </script> </html>