Primitive data types

Any value that you use is of a certain type. In JavaScript, the following are just a few primitive data types:

  1. Number: This includes floating point numbers as well as integers. For example, these values are all numbers-1, 100, 3.14.
  2. String: These consist of any number of characters, for example, a, one, and one 2 three.
  3. Boolean: This can be either true or false.
  4. Undefined: When you try to access a variable that doesn't exist, you get the special value undefined. The same happens when you declare a variable without assigning a value to it yet. JavaScript initializes the variable behind the scenes with the value undefined. The undefined data type can only have one value-the special value undefined.
  5. Null: This is another special data type that can have only one value-the null value. It means no value, an empty value, or nothing. The difference with undefined is that if a variable has a null value, it's still defined; it just so happens that its value is nothing. You'll see some examples shortly.

Any value that doesn't belong to one of the five primitive types listed here is an object. Even null is considered an object, which is a little awkward having an object (something) that is actually nothing. We'll learn more about objects in Chapter 4, Objects, but for the time being, just remember that in JavaScript, the data types are as follows:

  • Primitive (the five types listed previously)
  • Non-primitive (objects)

Finding out the value type - the typeof operator

If you want to know the type of a variable or a value, you can use the special typeof operator. This operator returns a string that represents the data type. The return values of using typeof are one of the following:

  • number
  • string
  • boolean
  • undefined
  • object
  • function

In the next few sections, you'll see typeof in action using examples of each of the five primitive data types.

Numbers

The simplest number is an integer. If you assign 1 to a variable, and then use the typeof operator, it returns the string number, as follows:

    > var n = 1; 
    > typeof n; 
    "number" 
    > n = 1234; 
    > typeof n; 
    "number" 

In the preceding example, you can see that the second time you set a variable's value, you don't need the var statement.

Numbers can also be floating point (decimals), for example:

    > var n2 = 1.23; 
    > typeof n; 
    "number" 

You can call typeof directly on the value without assigning it to a variable first, for example:

    > typeof 123; 
    "number" 

Octal and hexadecimal numbers

When a number starts with a 0, it's considered an octal number. For example, the octal 0377 is the decimal 255:

    > var n3 = 0377; 
    > typeof n3; 
    "number" 
    > n3; 
    255 

The last line in the preceding example prints the decimal representation of the octal value.

ES6 provides a prefix 0o (or 0O, but this looks very confusing in most monospace fonts) to represent octals. Consider the following line of code for example:

    console.log(0o776); //510 

While you may not be intimately familiar with octal numbers, you've probably used hexadecimal values to define colors in CSS stylesheets.

In CSS, you have several options to define a color, two of them are as follows:

  • Using decimal values to specify the amount of R (red), G (green), and B (blue), ranging from 0 to 255. For example, rgb(0, 0, 0) is black and rgb(255, 0, 0) is red (maximum amount of red and no green or blue).
  • Using hexadecimals and specifying two characters for each R, G, and B value. For example, #000000 is black and #ff0000 is red. This is because ff is the hexadecimal value for 255.

In JavaScript, you can put 0x before a hexadecimal value, also called hex for short, for example:

    > var n4 = 0x00; 
    > typeof n4; 
    "number" 
    > n4; 
    0 
    > var n5 = 0xff; 
    > typeof n5; 
    "number" 
    > n5; 
    255 

Binary Literals

Untill ES6, if you needed binary representation of an integer, you had to pass them to the parseInt() function as a string with a radix of 2, as follows:

    console.log(parseInt('111',2)); //7 

In ES6 you can use 0b (or 0B) prefix to represent binary integers. For example:

    console.log(0b111); //7 

Exponent literals

1e1 (also written as 1e+1 or 1E1 or 1E+1) represents the number 1 with a 0 after it, or in other words, 10. Similarly, 2e+3 represents the number 2 with three 0s after it, or 2000, for example:

    > 1e1; 
    10 
    > 1e+1; 
    10 
    > 2e+3; 
    2000 
    > typeof 2e+3; 
    "number" 

2e+3 means moving the decimal point three digits to the right of the number 2. There's also 2e-3, meaning you move the decimal point three digits to the left of the number 2. Look at the following figure:

Exponent literals

The following is the code:

    > 2e-3; 
    0.002 
    > 123.456E-3; 
    0.123456 
    > typeof 2e-3; 
    "number" 

Infinity

There is a special value in JavaScript called Infinity. It represents a number too big for JavaScript to handle. Infinity is indeed a number, as typing typeof Infinity in the console will confirm. You can also quickly check that a number with 308 zeros is ok, but 309 zeros is too much. To be precise, the biggest number JavaScript can handle is 1.7976931348623157e+308, while the smallest is 5e-324, Look at the following example:

    > Infinity; 
    Infinity 
    > typeof Infinity; 
    "number" 
    > 1e309; 
    Infinity 
    > 1e308; 
    1e+308 

Dividing by zero gives you infinity, for example:

    > var a = 6 / 0; 
    > a; 
    Infinity 

Infinity is the biggest number (or rather a little bigger than the biggest), but how about the smallest? It's infinity with a minus sign in front of it; -Infinity, for example:

    > var i = -Infinity; 
    > i; 
    -Infinity 
    > typeof i; 
    "number" 

Does this mean you can have something that's exactly twice as big as Infinity, from 0 up to infinity and then from 0 down to minus infinity? Well, not really. When you sum Infinity and -Infinity, you don't get 0, but something that is called Not a Number (NaN), For example:

    > Infinity - Infinity; 
    NaN 
    > -Infinity + Infinity; 
    NaN 

Any other arithmetic operation with Infinity as one of the operands gives you Infinity, for example:

    > Infinity - 20; 
    Infinity 
    > -Infinity * 3; 
    -Infinity 
    > Infinity / 2; 
    Infinity 
    > Infinity - 99999999999999999; 
    Infinity 

There is a lesser known global method, isFinite(), that tells you if the value is infinity or not. ES6 adds a Number.isFinite()method to do just that. Why another method, you may ask. The global variant of isFinite() tries to cast the value through Number(value), while Number.isFinite() doesn't, hence it's more accurate.

NaN

What was this NaN in the previous example? It turns out that despite its name, Not a Number, NaN is a special value that is also a number:

    > typeof NaN; 
    "number" 
    > var a = NaN; 
    > a; 
    NaN 

You get NaN when you try to perform an operation that assumes numbers, but the operation fails. For example, if you try to multiply 10 by the character "f", the result is NaN, because "f" is obviously not a valid operand for a multiplication:

    > var a = 10 * "f"; 
    > a;   
    NaN 

NaN is contagious, so if you have even one NaN in your arithmetic operation, the whole result goes down the drain, for example:

    > 1 + 2 + NaN; 
    NaN 

Number.isNaN

ES5 has a global method-isNaN(). It determines if a value is NaN or not. ES6 provides a very similar method-Number.isNaN() (Notice that this method is not global).

The difference between the global isNaN() and Number.isNaN() is that global isNaN() casts non-numeric values before evaluating them to be NaN. Let's look at the following example. We are using the ES6 Number.isNaN() method to test if something is a NaN or not:

    console.log(Number.isNaN('test')); //false : Strings are not NaN 
    console.log(Number.isNaN(123)); //false : integers are not NaN 
    console.log(Number.isNaN(NaN)); //true : NaNs are NaNs 
    console.log(Number.isNaN(123/'abc')); //true : 123/'abc' results in an NaN 

We saw that ES5's global isNaN() method first casts non-numeric values and then does the comparison; the following result will be different from its ES6 counterpart:

    console.log(isNaN('test')); //true 

In general, compared to its global variant, Number.isNaN() is more correct. However, neither of them can be used to figure out if something is not a number-they just answer if the value is a NaN or not. Practically, you are interested in knowing if a value identifies as a number or not. Mozilla suggests the following polyfill method to do just that:

    function isNumber(value) { 
      return typeof value==='number' && !Number.isNaN(value); 
    } 

Number.isInteger

This is a new method in ES6. It returns true if the number is finite and does not contain any decimal points (is an integer):

    console.log(Number.isInteger('test')); //false 
    console.log(Number.isInteger(Infinity)); //false 
    console.log(Number.isInteger(NaN)); //false 
    console.log(Number.isInteger(123)); //true 
    console.log(Number.isInteger(1.23)); //false 

Strings

A string is a sequence of characters used to represent text. In JavaScript, any value placed between single or double quotes is considered a string. This means that 1 is a number, but "1" is a string. When used with strings, typeof returns the string "string", for example:

    > var s = "some characters"; 
    > typeof s; 
    "string" 
    > var s = 'some characters and numbers 123 5.87'; 
    > typeof s; 
    "string" 

Here's an example of a number used in the string context:

    > var s = '1'; 
    > typeof s; 
    "string" 

If you put nothing in quotes, it's still a string (an empty string), for example:

    > var s = ""; typeof s; 
    "string" 

As you already know, when you use the plus sign with two numbers, this is the arithmetic addition operation. However, if you use the plus sign with strings, this is a string concatenation operation, and it returns the two strings glued together:

    > var s1 = "web";  
    > var s2 = "site";  
    > var s = s1 + s2;  
    > s; 
    "website" 
    > typeof s; 
    "string" 

The dual purpose of the + operator is a source of errors. Therefore, if you intend to concatenate strings, it's always best to make sure that all of the operands are strings. The same applies for addition; if you intend to add numbers then make sure the operands are numbers. You'll learn various ways to do so further in the chapter and the book.

String conversions

When you use a number-like string, for example, "1", as an operand in an arithmetic operation, the string is converted to a number behind the scenes. This works for all arithmetic operations except addition, because of its ambiguity. Consider the following example:

    > var s = '1';  
    > s = 3 * s;  
    > typeof s; 
    "number" 
    > s; 
    3 
    > var s = '1'; 
    > s++; 
    > typeof s; 
    "number" 
    > s; 
    2 

A lazy way to convert any number-like string to a number is to multiply it by 1 (another way is to use a function called parseInt(), as you'll see in the next chapter):

    > var s = "100"; typeof s; 
    "string" 
    > s = s * 1; 
    100 
    > typeof s; 
    "number" 

If the conversion fails, you'll get NaN:

    > var movie = '101 dalmatians'; 
    > movie * 1; 
    NaN 

You can convert a string to a number by multiplying it by 1. The opposite-converting anything to a string-can be done by concatenating it with an empty string, as follows:

    > var n = 1; 
    > typeof n; 
    "number" 
    > n = "" + n; 
    "1" 
    > typeof n; 
    "string" 

Special strings

There are also strings with special meanings, as listed in the following table:

String

Meaning

Example

\

'

"

The is the escape character. When you want to have quotes inside your string, you can escape them so that JavaScript doesn't think they mean the end of the string.

If you want to have an actual backslash in the string, escape it with another backslash.

> var s = 'I don't know';: This is an error because JavaScript thinks the string is I don and the rest is invalid code. The following codes are valid:

> var s = 'I don't know';   
> var s = "I don't know";   
> var s = "I don't know";   
> var s = '"Hello",   he said.';   
> var s = ""Hello",   he said.";   
Escaping the escape:   
> var s = "1\2"; s;   
"12"   

End of line.

> var s = '
1
2
3
';   
> s;   
"   
1   
2   
3   
"   

Carriage return.

Consider the following statements:

> var s = '1
2';   
> var s = '1

2';   
> var s = '1
2';   

The result of all of these is as follows:

> s;   
"1   
2"   

Tab.

> var s = "1	2";   
> s;   
"1 2"   

u

The u followed by a character code allows you to use Unicode.

Here's my name in Bulgarian written with Cyrillic characters:

> "u0421u0442u043Eu044Fu043D";   
"Стoян"   

There are also additional characters that are rarely used:  (backspace), v (vertical tab), and f (form feed).

String template literals

ES6 introduced template literals. If you are familiar with other programming languages, Perl and Python have supported template literals for a while now. Template literals allow expressions to be embedded within regular strings. ES6 has two kinds of literals: template literals and tagged literals.

Template literals are single or multiple line strings with embedded expressions. For example, you must have done something similar to this:

    var log_level="debug"; 
    var log_message="meltdown"; 
    console.log("Log level: "+ log_level + 
      " - message : " + log_message); 
    //Log level: debug - message : meltdown 

You can accomplish the same using template literals, as follows:

    console.log(`Log level: ${log_level} - message: ${log_message}`) 

Template literals are enclosed by the back-tick (``) (grave accent) character instead of the usual double or single quotes. Template literal place holders are indicated by the dollar sign and curly braces (${expression}). By default, they are concatenated to form a single string. The following example shows a template literal with a slightly complex expression:

    var a = 10; 
    var b = 10; 
    console.log(`Sum is ${a + b} and Multiplication would be ${a * b}.`);   
    //Sum is 20 and Multiplication would be 100. 

How about embedding a function call?

    var a = 10; 
    var b = 10; 
    function sum(x,y){ 
      return x+y 
    } 
    function multi(x,y){ 
      return x*y 
    } 
    console.log(`Sum is ${sum(a,b)} and Multiplication 
      would be ${multi(a,b)}.`); 

Template literals also simplify multiline string syntax. Instead of writing the following line of code:

    console.log("This is line one 
" + "and this is line two"); 

You can have a much cleaner syntax using template literals, which is as follows:

    console.log(`This is line one and this is line two`); 

ES6 has another interesting literal type called Tagged Template Literals. Tagged templates allow you to modify the output of template literals using a function. If you prefix an expression to a template literal, that prefix is considered to be a function to be invoked. The function needs to be defined before we can use the tagged template literal. For example, the following expression:

    transform`Name is ${lastname}, ${firstname} ${lastname}` 

The preceding expression is converted into a function call:

    transform([["Name is ", ", ", " "],firstname, lastname) 

The tag function, 'transform', gets two parameters-template strings like Name is and substitutions defined by ${}. The substitutions are only known at runtime. Let's expand the transform function:

    function transform(strings, ...substitutes){ 
      console.log(strings[0]); //"Name is" 
      console.log(substitutes[0]); //Bond 
    }   
    var firstname = "James"; 
    var lastname = "Bond" 
    transform`Name is ${lastname}, ${firstname} ${lastname}` 

When template strings (Name is) are passed to the tag function, there are two forms of each template string, as follows:

  • The raw form where the backslashes are not interpreted
  • The cooked form where the backslashes has special meaning

You can access the raw string form using raw property, as the following example shows:

    function rawTag(strings,...substitutes){ 
      console.log(strings.raw[0]) 
    } 
    rawTag`This is a raw text and 
 are not treated differently` 
    //This is a raw text and 
 are not treated differently 

Booleans

There are only two values that belong to the Boolean data type-the true and false values used without quotes:

    > var b = true;  
    > typeof b; 
    "boolean" 
    > var b = false;  
    > typeof b; 
    "boolean" 

If you quote true or false, they become strings, as shown in the following example:

    > var b = "true";  
    > typeof b; 
    "string" 

Logical operators

There are three operators, called logical operators, that work with Boolean values. These are as follows:

    ! - logical NOT (negation) 
    && - logical AND 
    || - logical OR 

You know that when something is not true, it must be false. Here's how this is expressed using JavaScript and the logical ! operator:

    > var b = !true; 
    > b; 
    false 

If you use the logical NOT twice, you will get the original value, which is as follows:

    > var b = !!true; 
    > b; 
    true 

If you use a logical operator on a non-Boolean value, the value is converted to Boolean behind the scenes, as follows:

    > var b = "one"; 
    > !b; 
    false 

In the preceding case, the string value "one" is converted to a Boolean, true, and then negated. The result of negating true is false. In the following example, there's a double negation, so the result is true:

    > var b = "one"; 
    > !!b; 
    true 

You can convert any value to its Boolean equivalent using a double negation. Understanding how any value converts to a Boolean is important. Most values convert to true with the exception of the following, which convert to false:

  • The empty string ""
  • null
  • undefined
  • The number 0
  • The number NaN
  • The Boolean false

These six values are referred to as falsy, while all others are truthy, (including, for example, the strings "0", " ", and "false").

Let's see some examples of the other two operators-the logical AND (&&) and the logical OR (||). When you use &&, the result is true only if all of the operands are true. When you use ||, the result is true if at least one of the operands is true:

    > var b1 = true, b2 = false; 
    > b1 || b2; 
    true 
    > b1 && b2; 
    false 

Here's a list of the possible operations and their results:

Operation

Result

true && true

true

true && false

false

false && true

false

false && false

false

true || true

true

true || false

true

false || true

true

false || false

false

You can use several logical operations one after the other, as follows:

    > true && true && false && true; 
    false 
    > false || true || false; 
    true 

You can also mix && and || in the same expression. In such cases, you should use parentheses to clarify how you intend the operation to work. Consider the following example:

    > false && false || true && true; 
    true 
    > false && (false || true) && true; 
    false 

Operator precedence

You might wonder why the previous expression (false && false || true && true) returned true. The answer lies in the operator precedence, as you know from mathematics:

    > 1 + 2 * 3; 
    7 

This is because multiplication has a higher precedence over addition, so 2 * 3 is evaluated first, as if you typed:

    > 1 + (2 * 3); 
    7 

Similarly for logical operations, ! has the highest precedence and is executed first, assuming there are no parentheses that demand otherwise. Then, in the order of precedence, comes && and finally, ||. In other words, the following two code snippets are the same. The first one is as follows:

    > false && false || true && true; 
    true 

And the second one is as follows:

    > (false && false) || (true && true); 
    true 

Note

Best practice

Use parentheses instead of relying on operator precedence. This makes your code easier to read and understand.

The ECMAScript standard defines the precedence of operators. While it may be a good memorization exercise, this book doesn't offer it. First of all, you'll forget it, and second, even if you manage to remember it, you shouldn't rely on it. The person reading and maintaining your code will likely be confused.

Lazy evaluation

If you have several logical operations one after the other, but the result becomes clear at some point before the end, the final operations will not be performed because they don't affect the end result. Consider the following line of code as an example:

    > true || false || true || false || true; 
    true 

As these are all OR operations and have the same precedence, the result will be true if at least, one of the operands is true. After the first operand is evaluated, it becomes clear that the result will be true, no matter what values follow. So, the JavaScript engine decides to be lazy (OK, efficient) and avoids unnecessary work by evaluating code that doesn't affect the end result. You can verify this short-circuiting behavior by experimenting in the console, as shown in the following code block:

    > var b = 5; 
    > true || (b = 6); 
    true 
    > b; 
    5 
    > true && (b = 6); 
    6 
    > b; 
    6 

This example also shows another interesting behavior-if JavaScript encounters a non-Boolean expression as an operand in a logical operation, the non-Boolean is returned as a result:

    > true || "something"; 
    true 
    > true && "something"; 
    "something" 
    > true && "something" && true; 
    true 

This behavior is not something you should rely on because it makes the code harder to understand. It's common to use this behavior to define variables when you're not sure whether they were previously defined. In the next example, if the mynumber variable is defined, its value is kept; otherwise, it's initialized with the value 10:

    > var mynumber = mynumber || 10; 
    > mynumber; 
    10 

This is simple and looks elegant, but be aware that it's not completely foolproof. If mynumber is defined and initialized to 0, or to any of the six falsy values, this code might not behave as you expect, as shown in the following piece of code:

    > var mynumber = 0; 
    > var mynumber = mynumber || 10; 
    > mynumber; 
    10 

Comparison

There's another set of operators that all return a Boolean value as a result of the operation. These are the comparison operators. The following table lists them together with example uses:

Operator symbol

Description

Example

==

Equality comparison: This returns true when both operands are equal. The operands are converted to the same type before being compared. They're also called loose comparison.

> 1 == 1;   
true   
> 1 == 2;   
false   
> 1 =='1';   
true   

===

Equality and type comparison: This returns true if both operands are equal and of the same type. It's better and safer to compare this way because there's no behind-the-scenes type conversions. It is also called strict comparison.

> 1 === '1';   
false   
> 1 === 1;   
true   

!=

Non-equality comparison: This returns true if the operands are not equal to each other (after a type conversion).

> 1 != 1;   
false   
> 1 != '1';   
false   
> 1 != '2';   
true   

!==

Non-equality comparison without type conversion: Returns true if the operands are not equal or if they are of different types.

> 1 !== 1;   
false   
> 1 !== '1';   
true   

>

This returns true if the left operand is greater than the right one.

> 1 > 1;   
false   
> 33 > 22;   
true   

>=

This returns true if the left operand is greater than or equal to the right one.

> 1 >= 1;   
true   

<

This returns true if the left operand is less than the right one.

> 1 < 1;   
false   
> 1 < 2;   
true   

<=

This returns true if the left operand is less than or equal to the right one.

> 1 <= 1;   
true   
> 1 <= 2;   
true   

Note that NaN is not equal to anything, not even itself. Take a look at the following line of code:

    > NaN == NaN; 
    false 

Undefined and null

If you try to use a non-existing variable, you'll get the following error:

    > foo; 
    ReferenceError: foo is not defined 

Using the typeof operator on a non-existing variable is not an error. You will get the "undefined" string back, as follows:

    > typeof foo; 
    "undefined" 

If you declare a variable without giving it a value, this is, of course, not an error. But, the typeof still returns "undefined":

    > var somevar; 
    > somevar; 
    > typeof somevar; 
    "undefined" 

This is because, when you declare a variable without initializing it, JavaScript automatically initializes it with the undefined value, as shown in the following lines of code:

    > var somevar; 
    > somevar === undefined; 
    true 

The null value, on the other hand, is not assigned by JavaScript behind the scenes; it's assigned by your code, which is as follows:

    > var somevar = null; 
    null 
    > somevar; 
    null 
    > typeof somevar; 
    "object" 

Although the difference between null and undefined is small, it can be critical at times. For example, if you attempt an arithmetic operation, you will get different results:

    > var i = 1 + undefined; 
    > i; 
    NaN 
    > var i = 1 + null; 
    > i; 
    1 

This is because of the different ways null and undefined are converted to the other primitive types. The following examples show the possible conversions:

  • Conversion to a number:
            > 1 * undefined; 
    
  • Conversion to NaN:
            > 1 * null; 
            0 
    
  • Conversion to a Boolean:
            > !!undefined; 
            false 
            > !!null; 
            false 
    
  • Conversion to a string:
            > "value: " + null; 
            "value: null" 
            > "value: " + undefined; 
            "value: undefined" 
    

Symbols

ES6 introduced a new primitive type-symbols. Several languages have a similar notion. Symbols look very similar to regular strings, but they are very different. Let's see how these symbols are created:

    var atom = Symbol() 

Notice that we don't use new operator while creating symbols. You will get an error when you do use it:

    var atom = new Symbol() //Symbol is not a constructor 

You can describe Symbol as well:

    var atom = Symbol('atomic symbol') 

Describing symbols comes in very handy while debugging large programs where there are lots of symbols scattered across.

The most important property of Symbol (and hence the reason of their existence) is that they are unique and immutable:

    console.log(Symbol() === Symbol()) //false 
    console.log(Symbol('atom') === Symbol('atom')) // false 

For now, we will have to pause this discussion on symbols. Symbols are used as property keys and places where you need unique identifiers. We will discuss symbols in a later part of this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.244.44