Literals, Variables, Constants, and Data Types - Learning JavaScript (2016)

Learning JavaScript (2016)

Chapter 3. Literals, Variables, Constants, and Data Types

This chapter is about data, and how we translate data into a format that JavaScript can understand.

You’re probably aware that all data is ultimately represented inside a computer as long sequences of ones and zeros—but for most day-to-day tasks, we want to think about data in a way that’s more natural to us: numbers, text, dates, and so on. We will call these abstractions data types.

Before we dive into the data types available in JavaScript, we will discuss variables, constants, and literals, which are the mechanisms available to us in JavaScript for holding data.

NOTE

The importance of vocabulary is often overlooked when you’re learning to program. While it may not seem important to understand how a literal differs from a value, or a statement from an expression, not knowing these terms will hamper your ability to learn. Most of these terms are not specific to JavaScript, but are commonly understood in computer science. Having a good grasp of the concepts is important, of course, but paying attention to vocabulary makes it easy for you to transfer your knowledge to other languages, and learn from more sources.

Variables and Constants

A variable is essentially a named value, and as the name implies, the value can change at any time. For example, if we’re working on a climate control system, we might have a variable called currentTempC:

let currentTempC = 22; // degrees Celsius

NOTE

The let keyword is new in ES6; prior to ES6, the only option was the var keyword, which we will discuss in Chapter 7.

This statement does two things: it declares (creates) the variable currentTempC and assigns it an initial value. We can change the value of currentTempC at any time:

currentTempC = 22.5;

Note that we don’t use let again; let specifically declares a variable, and you can only do it once.

TIP

With numbers, there’s no way to associate units with the value. That is, there’s no language feature that allows us to say that currentTempC is in degrees Celsius, thereby producing an error if we assign a value in degrees Fahrenheit. For this reason, I chose to add “C” to the variable name to make it clear that the units are degrees Celsius. The language can’t enforce this, but it’s a form of documentation that prevents casual mistakes.

When you declare a variable, you don’t have to provide it with an initial value. If you don’t, it implicitly gets a special value, undefined:

let targetTempC; // equivalent to "let targetTempC = undefined";

You can also declare multiple variables with the same let statement:

let targetTempC, room1 = "conference_room_a", room2 = "lobby";

In this example, we’ve declared three variables: targetTempC isn’t initialized with a variable, so it implicitly has the value undefined; room1 is declared with an initial value of "conference_room_a"; and room2 is declared with an initial value of "lobby". room1 and room2 are examples of string (text) variables.

A constant (new in ES6) also holds a value, but unlike a variable, can’t be changed after initialization. Let’s use a constant to express a comfortable room temperature and a maximum temp (const can also declare multiple constants):

const ROOM_TEMP_C = 21.5, MAX_TEMP_C = 30;

It is conventional (but not required) for constants that refer to a specific number or string to be named with all uppercase letters and underscores. This makes them easy to spot in your code, and is a visual cue that you shouldn’t try to change their value.

Variables or Constants: Which to Use?

In general, you should prefer constants over variables. You’ll find that more often than not you want a handy name for some piece of data, but you don’t need its value to change. The advantage of using constants is that it makes it harder to accidentally change the value of something that shouldn’t be changed. For example, if you’re working on a part of your program that performs some kind of action on a user, you might have a variable called user. If you’re dealing with only one user, it would probably indicate an error in your code if the value of user changed. If you were working with two users, you might call them user1 and user2, instead of simply reusing a single variable user.

So your rule of thumb should be to use a constant; if you find you have a legitimate need to change the value of the constant, you can always change it to a variable.

There is one situation in which you will always want to use variables instead of constants: variables used in loop control (which we’ll learn about in Chapter 4). Other situations where you might want to use variables are when the value of something is naturally changing over time (such astargetTempC or currentTemp in this chapter). If you get in the habit of preferring constants, however, you might be surprised by how seldom you really need a variable.

In the examples in this book, I have tried to use constants instead of variables whenever possible.

Identifier Names

Variable and constant names (as well as function names, which we’ll cover in Chapter 6) are called identifiers, and they have naming rules:

§ Identifiers must start with a letter, dollar sign ($), or underscore (_).

§ Identifiers consist of letters, numbers, the dollar sign ($), and underscore (_).

§ Unicode characters are allowed (for example, π or ö).

§ Identifiers cannot be a reserved word (see Appendix A).

Note that the dollar sign is not a special character the way it is in some other languages: it’s simply another character you can use in identifier names (many libraries, such as jQuery, have taken advantage of this, and used the dollar sign by itself as an identifier).

Reserved words are words that JavaScript might confuse with part of the language. For example, you can’t have a variable called let.

There’s no single convention for JavaScript identifiers, but the two most common are:

Camel case

currentTempC, anIdentifierName (so named because the capital letters look like the humps in a camel’s back).

Snake case

current_temp_c, an_identifier_name (slightly less popular).

You can use whichever convention you prefer, but consistency is a good idea: select one and stick with it. If you are working on a team or making your project available to a community, try choosing whatever the preferred convention is.

It is also advisable to adhere to the following conventions:

§ Identifiers shouldn’t start with a capital letter except for classes (which we’ll cover in Chapter 9).

§ Very often, identifiers that start with one or two underscores are used to represent special or “internal” variables. Unless you need to create your own special category of variables, avoid starting variable names with an underscore.

§ When using jQuery, identifiers that start with a dollar sign conventionally refer to jQuery-wrapped objects (see Chapter 19).

Literals

We’ve already seen some literals: when we gave currentTempC a value, we provided a numeric literal (22 at initialization, and 22.5 in the next example). Likewise, when we initialized room1, we provided a string literal ("conference_room_a"). The word literal means that you’re providing the value directly in the program. Essentially, a literal is a way to create a value; JavaScript takes the literal value you provide and creates a data value from it.

It’s important to understand the difference between a literal and an identifier. For example, think back to our earlier example where we created a variable called room1, which had the value "conference_room_a". room1 is an identifier (referring to a constant), and"conference_room_a" is a string literal (and also the value of room1). JavaScript is able to distinguish the identifier from the literal by the use of quotation marks (numbers don’t need any sort of quotation because identifiers can’t start with a number). Consider the following example:

let room1 = "conference_room_a"; // "conference_room_a" (in quotes) is

// a literal

let currentRoom = room1; // currentRoom now has the same value

// as room1 ("conference_room_a")

let currentRoom = conference_room_a; // produces an error; no identifier

// called conference_room_a exists

TIP

You can use a literal anywhere you can use an identifier (where a value is expected). For example, in our program, we could just use the numeric literal 21.5 everywhere instead of using ROOM_TEMP_C. If you use the numeric literal in a couple places, this may be OK. But if you use it in 10 or 100 places, you should be using a constant or variable instead: it makes your code easier to read, and you can change the value in one place instead of many.

It is up to you, the programmer, to decide what to make a variable and what to make a constant. Some things are quite obviously constants—such as the approximate value of π (the ratio of a circle’s circumference to its diameter), or DAYS_IN_MARCH. Other things, such as ROOM_TEMP_C, are not quite as obvious: 21.5°C might be a perfectly comfortable room temperature for me, but not for you, so if this value is configurable in your application, you would make it a variable instead.

Primitive Types and Objects

In JavaScript, values are either primitives or objects. Primitive types (such as string and number) are immutable. The number 5 will always be the number 5; the string "alpha" will always be the string "alpha". This seems obvious for numbers, but it often trips people up with strings: when people concatenate strings together ("alpha" + "omega"), they sometimes think it’s the same string, just modified. It is not: it is a new string, in the same way that 6 is a different number than 5. There are six primitive types that we will cover:

§ Number

§ String

§ Boolean

§ Null

§ Undefined

§ Symbol

Note that immutability doesn’t mean the contents of a variable can’t change:

let str = "hello";

str = "world";

First str is initialized with the (immutable) value "hello", and then it is assigned a new (immutable) value, "world". What’s important here is that "hello" and "world" are different strings; only the value that str holds has changed. Most of the time, this distinction is academic, but the knowledge will come in handy later when we discuss functions in Chapter 6.

In addition to these six primitive types, there are objects. Unlike primitives, objects can take on different forms and values, and are more chameleon-like.

Because of their flexibility, objects can be used to construct custom data types. As a matter of fact, JavaScript provides some built-in object types. The built-in object types we’ll cover are as follows:

§ Array

§ Date

§ RegExp

§ Map and WeakMap

§ Set and WeakSet

Lastly, the primitive types number, string, and boolean have corresponding object types, Number, String, and Boolean. These corresponding objects don’t actually store a value (that’s what the primitive does), but rather provide functionality that’s related to the corresponding primitive. We will discuss these object types along with their primitives.

Numbers

While some numbers (like 3, 5.5, and 1,000,000) can be represented accurately by a computer, many numbers are necessarily approximations. For example, π cannot be represented by a computer at all, because its digits are infinite and do not repeat. Other numbers, such as 1/3, can be represented by special techniques, but because of their forever repeating decimals (3.33333…), they are normally approximated as well.

JavaScript—along with most other programming languages—approximates real numbers through a format called IEEE-764 double-precision floating-point (which I will refer to simply as a “double” from here on out). The details of this format are beyond the scope of this book, but unless you are doing sophisticated numerical analysis, you probably don’t need to understand them. However, the consequences of the approximations required by this format often catch people off guard. For example, if you ask JavaScript to calculate 0.1 + 0.2, it will return 0.30000000000000004. This does not mean that JavaScript is “broken” or bad at math: it’s simply an unavoidable consequence of approximating infinite values in finite memory.

JavaScript is an unusual programming language in that it only has this one numeric data type.1 Most languages have multiple integer types and two or more floating-point types. On one hand, this choice simplifies JavaScript, especially for beginners. On the other hand, it reduces JavaScript’s suitability for certain applications that require the performance of integer arithmetic, or the precision of fixed-precision numbers.

JavaScript recognizes four types of numeric literal: decimal, binary, octal, and hexadecimal. With decimal literals, you can express integers (no decimal), decimal numbers, and numbers in base-10 exponential notation (an abbreviation of scientific notation). In addition, there are special values for infinity, negative infinity, and “not a number” (these are not technically numeric literals, but they do result in numeric values, so I am including them here):

let count = 10; // integer literal; count is still a double

const blue = 0x0000ff; // hexadecimal (hex ff = decimal 255)

const umask = 0o0022; // octal (octal 22 = decimal 18)

const roomTemp = 21.5; // decimal

const c = 3.0e6; // exponential (3.0 × 10^6 = 3,000,000)

const e = -1.6e-19; // exponential (-1.6 × 10^-19 = 0.00000000000000000016)

const inf = Infinity;

const ninf = -Infinity;

const nan = NaN; // "not a number"

TIP

No matter what literal format you use (decimal, hexadecimal, exponential, etc.), the number that gets created is stored in the same format: a double. The various literal formats simply allow you to specify a number in whatever format is convenient. JavaScript has limited support for displaying numbers in different formats, which we’ll discuss in Chapter 16.

The mathematicians in the crowd might be calling foul: infinity is not a number! Indeed, it isn’t; but of course, neither is NaN. These are not numbers you do computation with; rather, they are available as placeholders.

In addition, there are some useful properties of the corresponding Number object that represent important numeric values:

const small = Number.EPSILON; // the smallest value that can be

// added to 1 to get a distinct number

// larger than 1, approx. 2.2e-16

const bigInt = Number.MAX_SAFE_INTEGER; // the largest representable integer

const max = Number.MAX_VALUE; // the largest representable number

const minInt = Number.MIN_SAFE_INTEGER; // the smallest representable integer

const min = Number.MIN_VALUE; // the smallest representable number

const nInf = Number.NEGATIVE_INFINITY; // the same as -Infinity

const nan = Number.NaN; // the same as NaN

const inf = Number.POSITIVE_INFINITY; // the same as Infinity

We’ll discuss the importance of these values in Chapter 16.

Strings

A string is simply text data (the word string comes from “string of characters”—a word originally used in the late 1800s by typesetters, and then later by mathematicians, to represent a sequence of symbols in a definite order).

Strings in JavaScript represent Unicode text. Unicode is a computing industry standard for representing text data, and includes code points for every character or symbol in most known human languages (including “languages” that might surprise you, such as Emoji). While Unicode itself is capable of representing text in any language, that does not mean that the software rendering the Unicode will be capable of rendering every code point correctly. In this book, we’ll stick to fairly common Unicode characters that are most likely available in your browser and console. If you are working with exotic characters or languages, you’ll want to do additional research on Unicode to understand how code points are rendered.

In JavaScript, string literals are represented with single quotes, double quotes, or backticks.2 The backtick was introduced in ES6 to enable template strings, which we will cover shortly.

Escaping

When you’re trying to represent text data in a program that’s made up of text data, the problem is always distinguishing text data from the program itself. Setting off strings within quotes is a start, but what if you want to use quotes in a string? To solve this problem, there needs to be a method of escaping characters so they are not taken as string termination. Consider the following examples (which do not require escaping):

const dialog = 'Sam looked up, and said "hello, old friend!", as Max walked in.';

const imperative = "Don't do that!";

In dialog, we can use double quotes without fear because our string is set off with single quotes. Likewise, in imperative, we can use an apostrophe because the string is set off with double quotes. But what if we needed to use both? Consider:

// this will produce an error

const dialog = "Sam looked up and said "don't do that!" to Max.";

This dialog string will fail no matter which quotation mark we choose. Fortunately, we can escape quotation marks with a backslash (\), which is a signal to JavaScript that the string is not ending. Here’s the preceding example rewritten to use both types of quotation marks:

const dialog1 = "He looked up and said \"don't do that!\" to Max.";

const dialog2 = 'He looked up and said "don\'t do that!" to Max.';

Then, of course, we get into the chicken-and-egg problem that arises when we want to use a backslash in our string. To solve this problem, a backslash can escape itself:

const s = "In JavaScript, use \\ as an escape character in strings.";

Whether you use single or double quotes is up to you. I generally prefer double quotation marks when I’m writing text that might be presented to the user, because I use contractions (like don’t) more often than I use double quotation marks. When I’m expressing HTML inside a JavaScript string, I tend to prefer single quotation marks so that I can use double quotation marks for attribute values.

Special Characters

The backslash is used for more than simply escaping quotation marks: it is also used to represent certain nonprintable characters, such as newlines and arbitrary Unicode characters. Table 3-1 lists the commonly used special characters.

Code

Description

Example

\n

Newline (technically a line feed character: ASCII/Unicode 10)

"Line1\nLine2"

\r

Carriage return (ASCII/Unicode 13)

"Windows line 1\r\nWindows line 2"

\t

Tab (ASCII/Unicode 9)

"Speed:\t60kph"

\'

Single quote (note that you can use this even when not necessary)

"Don\'t"

\"

Double quote (note that you can use this even when not necessary)

'Sam said \"hello\".'

\`

Backtick (or “accent grave”; new in ES6)

`New in ES6: \` strings.`

\$

Dollar sign (new in ES6)

`New in ES6: ${interpolation}`

\\

Backslash

"Use \\\\ to represent \\!"

\uXXXX

Arbitrary Unicode code point (where +XXXX+ is a hexadecimal code point)

"De Morgan’s law: \u2310(P \u22c0 Q) \u21D4 (\u2310P) \u22c1 (\u2310Q)"

\xXX

Latin-1 character (where +XX+ is a hexadecimal Latin-1 code point)

"\xc9p\xe9e is fun, but foil is more fun."

Table 3-1. Commonly used special characters

Note that the Latin-1 character set is a subset of Unicode, and any Latin-1 character \xXX can be represented by the equivalent Unicode code point \u00XX. For hexadecimal numbers, you may use lowercase or uppercase letters as you please; I personally favor lowercase, as I find them easier to read.

You don’t need to use escape codes for Unicode characters; you can also enter them directly into your editor. The way to access Unicode characters varies among editors and operating systems (and there is usually more than one way); please consult your editor or operating system documentation if you wish to enter Unicode characters directly.

Additionally, there are some rarely used special characters, shown in Table 3-2. To my recollection, I have never used any of these in a JavaScript program, but I include them here for the sake of completeness.

Code

Description

Example

\0

The NUL character (ASCII/Unicode 0)

"ASCII NUL: \0"

\v

Vertical tab (ASCII/Unicode 11)

"Vertical tab: \v"

\b

Backspace (ASCII/Unicode 8)

"Backspace: \b"

\f

Form feed (ASCII/Unicode 12)

"Form feed: \f"

Table 3-2. Rarely used special characters

Template Strings

A very common need is to express values in a string. This can be accomplished through a mechanism called string concatenation:

let currentTemp = 19.5;

// 00b0 is the Unicode code point for the "degree" symbol

const message = "The current temperature is " + currentTemp + "\u00b0C";

Up until ES6, string concatenation was the only way to accomplish this (short of using a third-party library). ES6 introduces string templates (also known as string interpolation). String templates provide a shorthand way of injecting values into a string. String templates use backticks instead of single or double quotes. Here is the previous example rewritten using string templates:

let currentTemp = 19.5;

const message = `The current temperature is ${currentTemp}\u00b0C`;

Inside a string template, the dollar sign becomes a special character (you can escape it with a backslash): if it’s followed by a value3 wrapped in curly braces, that value is inserted into the string.

Template strings are one of my favorite features of ES6, and you’ll see them used throughout this book.

Multiline Strings

Before ES6, multiline string support was spotty at best. The language specification allows for escaping the newline at the end of a line of source code, but it’s a feature I’ve never used due to unreliable browser support. With ES6, the feature is more likely to be available, but there are some quirks that you should be aware of. Note that these techniques probably won’t work in a JavaScript console (like the one in your browser) so you’ll have to actually write a JavaScript file to try these out. For single- and double-quoted strings, you can escape the newline thusly:

const multiline = "line1\

line2";

If you expect multiline to be a string with a newline in it, you’ll be surprised: the slash at the end of the line escapes the newline, but does not insert a newline into the string. So the result will be "line1line2". If you want an actual newline, you’ll have to do this:

const multiline = "line1\n\

line2";

Backtick strings behave a little more like you might expect:

const multiline = `line1

line2`;

This will result in a string with a newline. With both techniques, however, any indentation at the beginning of the line will be included in the resulting string. For example, the following will result in a string with newlines and whitespace before line2 and line3, which may not be desirable:

const multiline = `line1

line2

line3`;

For this reason, I avoid multiline string syntax: it forces me to either abandon indentation that makes code easier to read, or include whitespace in my multiline strings that I may not want. If I do want to break strings up over multiple lines of source code, I usually use string concatenation:

const multiline = "line1\n" +

"line2\n" +

"line3";

This allows me to indent my code in an easy-to-read fashion, and get the string I want. Note that you can mix and match types of strings in string concatenation:

const multiline = 'Current temperature:\n' +

`\t${currentTemp}\u00b0C\n` +

"Don't worry...the heat is on!";

Numbers as Strings

If you put a number in quotation marks, it’s not a number—it’s a string. That said, JavaScript will automatically convert strings that contain numbers to numbers as necessary. When and how this happens can be very confusing, as we will discuss in Chapter 5. Here’s an example that illustrates when this conversion happens, and when it doesn’t:

const result1 = 3 + '30'; // 3 is converted to a string; result is string '330'

const result2 = 3 * '30'; // '30' is converted to a number; result is numeric 90

As a rule of thumb, when you want to use numbers, use numbers (that is, leave off the quotes), and when you want to use strings, use strings. The gray area is when you’re accepting user input, which almost always comes as a string, leaving it up to you to convert to a number where appropriate. Later in this chapter, we will discuss techniques for converting among data types.

Booleans

Booleans are value types that have only two possible values: true and false. Some languages (like C) use numbers instead of booleans: 0 is false and every other number is true. JavaScript has a similar mechanism, allowing any value (not just numbers) to be considered “truthy” or “falsy,” which we’ll discuss further in Chapter 5.

Be careful not to use quotation marks when you intend to use a boolean. In particular, a lot of people get tripped up by the fact that the string "false" is actually truthy! Here’s the proper way to express boolean literals:

let heating = true;

let cooling = false;

Symbols

New in ES6 are symbols: a new data type representing unique tokens. Once you create a symbol, it is unique: it will match no other symbol. In this way, symbols are like objects (every object is unique). However, in all other ways, symbols are primitives, lending themselves to useful language features that allow extensibility, which we’ll learn more about in Chapter 9.

Symbols are created with the Symbol() constructor.4 You can optionally provide a description, which is just for convenience:

const RED = Symbol();

const ORANGE = Symbol("The color of a sunset!");

RED === ORANGE // false: every symbol is unique

I recommend using symbols whenever you want to have a unique identifier that you don’t want inadvertently confused with some other identifier.

null and undefined

JavaScript has two special types, null and undefined. null has only one possible value (null), and undefined has only one possible value (undefined). Both null and undefined represent something that doesn’t exist, and the fact that there are two separate data types has caused no end of confusion, especially among beginners.

The general rule of thumb is that null is a data type that is available to you, the programmer, and undefined should be reserved for JavaScript itself, to indicate that something hasn’t been given a value yet. This is not an enforced rule: the undefined value is available to the programmer to use at any time, but common sense dictates that you should be extremely cautious in using it. The only time I explicitly set a variable to undefined is when I want to deliberately mimic the behavior of a variable that hasn’t been given a value yet. More commonly, you want to express that the value of a variable isn’t known or isn’t applicable, in which case null is a better choice. This may seem like splitting hairs, and sometimes it is—the beginning programmer is advised to use null when unsure. Note that if you declare a variable without explicitly giving it a value, it will have a value of undefined by default. Here are examples of using null and undefined literals:

let currentTemp; // implicit value of undefined

const targetTemp = null; // target temp null -- "not yet known"

currentTemp = 19.5; // currentTemp now has value

currentTemp = undefined; // currentTemp appears as if it had never

// been initialized; not recommended

Objects

Unlike the immutable primitive types, which only ever represent one value, objects can represent multiple or complex values, and can change over their lifetime. In essence, an object is a container, and the contents of that container can change over time (it’s the same object with different contents). Like the primitive types, objects have a literal syntax: curly braces ({ and }). Because curly braces come in pairs, it allows us to express an object’s contents. Let’s start with an empty object:

const obj = {};

NOTE

We can name our object anything we want, and normally you would use a descriptive name, such as user or shoppingCart. We’re just learning the mechanics of objects, and our example doesn’t represent anything specific so we just generically call it obj.

The contents of an object are called properties (or members), and properties consist of a name (or key) and value. Property names must be strings or symbols, and values can be any type (including other objects). Let’s add a property color to obj:

obj.size; // undefined

obj.color; // "yellow"

To use the member access operator, the property name must be a valid identifier. If you want property names that are not valid identifiers, you have to use the computed member access operator (you can also use this for valid identifiers):

obj["not an identifier"] = 3;

obj["not an identifier"]; // 3

obj["color"]; // "yellow"

You also use the computed member access operator for symbol properties:

const SIZE = Symbol();

obj[SIZE] = 8;

obj[SIZE]; // 8

At this point, obj contains three properties with keys "color" (a string that is a valid identifier), "not an identifier" (a string that is not a valid identifier), and SIZE (a symbol).

NOTE

If you’re following along in a JavaScript console, you may notice that the console doesn’t list the SIZE symbol as a property of obj. It is (you can verify this by typing obj[SIZE]), but symbol properties are handled differently and are not displayed by default. Also note that the key for this property is the symbol SIZE, not the string "SIZE". You can verify this by typingobj.SIZE = 0 (the member access property always operates on string properties) and then obj[SIZE] and obj.SIZE (or obj["SIZE"]).

At this juncture, let’s pause and remind ourselves of the differences between primitives and objects. Throughout this section, we have been manipulating and modifying the object contained by the variable obj, but obj has been pointing to the same object all along. If obj had instead contained a string or a number or any other primitive, it would be a different primitive value every time we change it. In other words, obj has pointed to the same object all along, but the object itself has changed.

In the instance of obj, we created an empty object, but the object literal syntax also allows us to create an object that has properties right out of the gate. Inside the curly braces, properties are separated by commas, and the name and value are separated by a colon:

const sam1 = {

name: 'Sam',

age: 4,

};

const sam2 = { name: 'Sam', age: 4 }; // declaration on one line

const sam3 = {

name: 'Sam',

classification: { // property values can

kingdom: 'Anamalia', // be objects themselves

phylum: 'Chordata',

class: 'Mamalia',

order: 'Carnivoria',

family: 'Felidae',

subfaimily: 'Felinae',

genus: 'Felis',

species: 'catus',

},

};

In this example, we’ve created three new objects that demonstrate the object literal syntax. Note that the properties contained by sam1 and sam2 are the same; however, they are two distinct objects (again, contrast to primitives: two variables that both contain the number 3 refer to the same primitive). In sam3, property classification is itself an object. Consider the different ways we can access Sam the cat’s family (it also doesn’t matter if we use single or double quotes or even backticks):

sam3.classification.family; // "Felinae"

sam3["classification"].family; // "Felinae"

sam3.classification["family"]; // "Felinae"

sam3["classification"]["family"]; // "Felinae"

Objects can also contain functions. We’ll learn about functions in depth in Chapter 6, but for now, what you need to know is that a function contains code (essentially a subprogram). Here’s how we add a function to sam3:

sam3.speak = function() { return "Meow!"; };

We can now call that function by adding parentheses to it:

sam3.speak(); // "Meow!"

Lastly, we can delete a property from an object with the delete operator:

delete sam3.classification; // the whole classification tree is removed

delete sam3.speak; // the speak function is removed

If you’re familiar with object-oriented programming (OOP), you may be wondering how JavaScript objects relate to OOP. For now, you should think of an object as a generic container; we will discuss OOP in Chapter 9.

Number, String, and Boolean Objects

We mentioned earlier in this chapter that numbers, strings, and booleans have corresponding object types (Number, String, and Boolean). These objects serve two purposes: to store special values (such as Number.INFINITY), and to provide functionality in the form of function. Consider the following:

const s = "hello";

s.toUpperCase(); // "HELLO"

This example makes it look like s is an object (we accessed a function property as if it were). But we know better: s is a primitive string type. So how is this happening? What JavaScript is doing is creating a temporary String object (which has a function toUpperCase, among others). As soon as the function has been called, JavaScript discards the object. To prove the point, let’s try to assign a property to a string:

const s = "hello";

s.rating = 3; // no error...success?

s.rating; // undefined

JavaScript allows us to do this, making it seem like we’re assigning a property to the string s. What’s really happening, though, is that we’re assigning a property to the temporary String object that’s created. That temporary object is immediately discarded, which is why s.rating isundefined.

This behavior will be transparent to you, and rarely (if ever) do you have to think about it, but it can be useful to know what JavaScript is doing behind the scenes.

Arrays

In JavaScript, arrays are a special type of object. Unlike regular objects, array contents have a natural order (element 0 will always come before element 1), and keys are numeric and sequential. Arrays support a number of useful methods that make this data type an extremely powerful way to express information, which we will cover in Chapter 8.

If you’re coming from other languages, you’ll find that arrays in JavaScript are something of a hybrid of the efficient, indexed arrays of C and more powerful dynamic arrays and linked lists. Arrays in JavaScript have the following properties:

§ Array size is not fixed; you can add or remove elements at any time.

§ Arrays are not homogeneous; each individual element can be of any type.

§ Arrays are zero-based. That is, the first element in the array is element 0.

WARNING

Because arrays are special types of objects with some extra functionality, you can assign non-numeric (or fractional or negative) keys to an array. While this is possible, it contradicts the intended purpose of arrays, can lead to confusing behavior and difficult-to-diagnose bugs, and is best avoided.

To create an array literal in JavaScript, use square brackets, with the elements of the array separated by commas:

const a1 = [1, 2, 3, 4]; // array containing numbers

const a2 = [1, 'two', 3, null]; // array containing mixed types

const a3 = [ // array on multiple lines

"What the hammer? What the chain?",

"In what furnace was thy brain?",

"What the anvil? What dread grasp",

"Dare its deadly terrors clasp?",

];

const a4 = [ // array containing objects

{ name: "Ruby", hardness: 9 },

{ name: "Diamond", hardness: 10 },

{ name: "Topaz", hardness: 8 },

];

const a5 = [ // array containing arrays

[1, 3, 5],

[2, 4, 6],

];

Arrays have a property length, which returns the number of elements in the array:

const arr = ['a', 'b', 'c'];

arr.length; // 3

To access individual elements of an array, we simply use the numeric index of the element inside square brackets (similar to how we access properties on an object):

const arr = ['a', 'b', 'c'];

// get the first element:

arr[0]; // 'a'

// the index of the last element in arr is arr.length-1:

arr[arr.length - 1]; // 'c'

To overwrite the value at a specific array index, you can simply assign to it:5

const arr = [1, 2, 'c', 4, 5];

arr[2] = 3; // arr is now [1, 2, 3, 4, 5]

In Chapter 8, we’ll learn many more techniques for modifying arrays and their contents.

Trailing Commas in Objects and Arrays

The alert reader may have already noticed that in these code samples, when the content of objects and arrays spans multiple lines, there is a trailing (or dangling or terminal) comma:

const arr = [

"One",

"Two",

"Three",

];

const o = {

one: 1,

two: 2,

three: 3,

};

Many programmers avoid adding this because in early versions of Internet Explorer, trailing commas produced an error (even though it has always been allowed in the JavaScript syntax). I prefer trailing commas because I am frequently cutting and pasting within arrays and objects, and adding things to the end of the object, so having the trailing comma means I never need to remember to add a comma on the line before; it’s simply always there. This is a hotly contested convention, and my preference is just that: a preference. If you find the trailing comma troubling (or your team’s style guide prohibits its use), by all means, omit it.

NOTE

JavaScript Object Notation (JSON), a JavaScript-like data syntax used quite frequently, does not allow trailing commas.

Dates

Dates and times in JavaScript are represented by the built-in Date object. Date is one of the more problematic aspects of the language. Originally a direct port from Java (one of the few areas in which JavaScript actually has any direct relationship to Java), the Date object can be difficult to work with, especially if you are dealing with dates in different time zones.

To create a date that’s initialized to the current date and time, use new Date():

const now = new Date();

now; // example: Thu Aug 20 2015 18:31:26 GMT-0700 (Pacific Daylight Time)

To create a date that’s initialized to a specific date (at 12:00 a.m.):

const halloween = new Date(2016, 9, 31); // note that months are

// zero-based: 9=October

To create a date that’s initialized to a specific date and time:

const halloweenParty = new Date(2016, 9, 31, 19, 0); // 19:00 = 7:00 pm

Once you have a date object, you can retrieve its components:

halloweenParty.getFullYear(); // 2016

halloweenParty.getMonth(); // 9

halloweenParty.getDate(); // 31

halloweenParty.getDay(); // 1 (Mon; 0=Sun, 1=Mon,...)

halloweenParty.getHours(); // 19

halloweenParty.getMinutes(); // 0

halloweenParty.getSeconds(); // 0

halloweenParty.getMilliseconds(); // 0

We will cover dates in detail in Chapter 15.

Regular Expressions

A regular expression (or regex or regexp) is something of a sublanguage of JavaScript. It is a common language extension offered by many different programming languages, and it represents a compact way to perform complex search and replace operations on strings. Regular expressions will be covered in Chapter 17. Regular expressions in JavaScript are represented by the RegExp object, and they have a literal syntax consisting of symbols between a pair of forward slashes. Here are some examples (which will look like gibberish if you’ve never seen a regex before):

// extremely simple email recognizer

const email = /\b[a-z0-9._-]+@[a-z_-]+(?:\.[a-z]+)+\b/;

// US phone number recognizer

const phone = /(:?\+1)?(:?\(\d{3}\)\s?|\d{3}[\s-]?)\d{3}[\s-]?\d{4}/;

Maps and Sets

ES6 introduces the data types Map and Set, and their “weak” counterparts, WeakMap and WeakSet. Maps, like objects, map keys to values, but offer some advantages over objects in certain situations. Sets are similar to arrays, except they can’t contain duplicates. The weak counterparts function similarly, but they make functionality trade-offs in exchange for more performance in certain situations.

We will cover maps and sets in Chapter 10.

Data Type Conversion

Converting between one data type and another is a very common task. Data that comes from user input or other systems often has to be converted. This section covers some of the more common data conversion techniques.

Converting to Numbers

It’s very common to want to convert strings to numbers. When you collect input from a user, it’s usually as a string, even if you’re collecting a numeric value from them. JavaScript offers a couple of methods to convert strings to numbers. The first is to use the Number object constructor:6

const numStr = "33.3";

const num = Number(numStr); // this creates a number value, *not*

// an instance of the Number object

If the string can’t be converted to a number, NaN will be returned.

The second approach is to use the built-in parseInt or parseFloat functions. These behave much the same as the Number constructor, with a couple of exceptions. With parseInt, you can specify a radix, which is the base with which you want to parse the number. For example, this allows you to specify base 16 to parse hexadecimal numbers. It is always recommended you specify a radix, even if it is 10 (the default). Both parseInt and parseFloat will discard everything they find past the number, allowing you to pass in messier input. Here are examples:

const a = parseInt("16 volts", 10); // the " volts" is ignored, 16 is

// parsed in base 10

const b = parseInt("3a", 16); // parse hexadecimal 3a; result is 58

const c = parseFloat("15.5 kph"); // the " kph" is ignored; parseFloat

// always assumes base 10

A Date object can be converted to a number that represents the number of milliseconds since midnight, January 1, 1970, UTC, using its valueOf() method:

const d = new Date(); // current date

const ts = d.valueOf(); // numeric value: milliseconds since

// midnight, January 1, 1970 UTC

Sometimes, it is useful to convert boolean values to 1 (true) or 0 (false). The conversion uses the conditional operator (which we will learn about in Chapter 5):

const b = true;

const n = b ? 1 : 0;

Converting to String

All objects in JavaScript have a method toString(), which returns a string representation. In practice, the default implementation isn’t particularly useful. It works well for numbers, though it isn’t often necessary to convert a number to a string: that conversion usually happens automatically during string concatenation or interpolation. But if you ever do need to convert a number to a string value, the toString() method is what you want:

const n = 33.5;

n; // 33.5 - a number

const s = n.toString();

s; // "33.5" - a string

Date objects implement a useful (if lengthy) toString() implementation, but most objects will simply return the string "[object Object]". Objects can be modified to return a more useful string representation, but that’s a topic for Chapter 9. Arrays, quite usefully, take each of their elements, convert them to strings, and then join those strings with commas:

const arr = [1, true, "hello"];

arr.toString(); // "1,true,hello"

Converting to Boolean

In Chapter 5, we’ll learn about JavaScript’s idea of “truthy” and “falsy,” which is a way of coercing all values to true or false, so we won’t go into all those details here. But we will see that we can convert any value to a boolean by using the “not” operator (!) twice. Using it once converts the value to a boolean, but the opposite of what you want; using it again converts it to what you expect. As with numeric conversion, you can also use the Boolean constructor (again, without the new keyword) to achieve the same result:

const n = 0; // "falsy" value

const b1 = !!n; // false

const b2 = Boolean(n); // false

Conclusion

The data types available to you in a programming language are your basic building blocks for the kind of things you can express in the language. For most of your day-to-day programming, the key points you want to take away from this chapter are as follows:

§ JavaScript has six primitive types (string, number, boolean, null, undefined, and symbol) and an object type.

§ All numbers in JavaScript are double-precision floating-point numbers.

§ Arrays are special types of objects, and along with objects, represent very powerful and flexible data types.

§ Other data types you will be using often (dates, maps, sets, and regular expressions) are special types of objects.

Most likely, you will be using strings quite a lot, and I highly recommend that you make sure you understand the escaping rules for strings, and how string templates work, before proceeding.

1This may change in the future: dedicated integer types are an oft-discussed language feature.

2Also called a grave accent mark.

3You can actually use any expression inside the curly braces. We will cover expressions in Chapter 5.

4If you’re already familiar with object-oriented programming in JavaScript, note that creating a symbol with the new keyword is not allowed, and is an exception to the convention that identifiers that start with capital letters should be used with new.

5If you assign an index that is equal to or larger than the length of the array, the size of the array will increase to accommodate the new element.

6Normally, you wouldn’t use a constructor without the new keyword, which we’ll learn about in Chapter 9; this is a special case.