Chapter 4

Other Native Objects

Now that we’ve taken a look at strings and arrays, we’ll continue with a tour of some other important JavaScript objects: math, dates, regular expressions, and generic objects.

4.1 Math and Number

Like most programming languages, JavaScript supports a large number of mathematical operations:

> 1 + 1;
2
> 2 - 3;
-1
> 2 * 3;
6
> 10/5;
2 > 2/3;
0.6666666666666666

Note that the final example here isn’t exact; it’s a floating-point number (also called a float), which can’t be represented exactly by the computer. But in fact JavaScript has only one numerical type, and even something like 1 or 2 is treated as floating point under the hood. This is convenient for us as programmers, since it means we never have to make distinctions between different kinds of numbers.1

1. In contrast to JavaScript, many languages distinguish between integers and floats, which leads to pitfalls like 1.0/2.0 being the expected 0.5, but 1/2 being 0.

Many programmers, including me, find it convenient to fire up a REPL and use it as a simple calculator when the need arises. It’s not fancy, but it’s quick and relatively powerful, and the ability to define variables often comes in handy as well.

4.1.1 More Advanced Operations

JavaScript supports more advanced mathematical operations via a global object called Math, which has properties and methods for things like mathematical constants, exponentiation (powers),2 roots, and trigonometric functions:

2. Adding support for exponentiation with two asterisks ** is in the works but isn’t universally implemented as of this writing.

> Math.PI
3.141592653589793
> Math.pow(2, 3);
8
> Math.sqrt(3)
1.7320508075688772
> Math.cos(2*Math.PI)
1

There is one gotcha for those coming from high-school (and even college) textbooks that use ln for the natural logarithm. Like mathematicians and most other programming languages, JavaScript uses log instead:

> Math.E;
2.718281828459045
> Math.log(Math.E);
1
> Math.log(10);
2.302585092994046

Mathematicians typically indicate base-ten logarithms using log10, and JavaScript follows suit with log10:

> Math.log10(10);
1
> Math.log10(1000000);
6
> Math.log10(Math.E);
0.4342944819032518

The Math documentation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math) includes a more comprehensive list of further operations.

4.1.2 Math to String

We discussed in Chapter 3 how to get from strings to arrays (and vice versa) using split and join. Similarly, JavaScript allows us to convert between numbers and strings.

Probably the most common way to convert from a number to a string is using the toString() method, as we can see with this useful definition (https://tauday.com/tau-manifesto) (Figure 4.1):3

3. The use of τ to represent the circle constant 6.283185 … was proposed in a math essay I published in 2010 called The Tau Manifesto, which also established a math holiday called Tau Day (https://tauday.com/), celebrated annually on June 28.

> let tau = 2 * Math.PI;
> tau.toString();
'6.283185307179586'
images

Figure 4.1: Some special angles in terms of τ = 2π.

The toString() method won’t work on a bare integer:

> 100.toString();
100.toString();
^^^^

SyntaxError: Invalid or unexpected token

But it will work if you use an extra dot, so that JavaScript treats the number as a float:

> 100.0.toString()
'100'

This is unfortunate behavior, since the string corresponding to 100.0 should more properly be "100.0", but this is a price we pay for JavaScript’s lack of a proper integer data type.

Another way to convert raw numbers to strings is to use the String object directly:

> String(100.0);
'100.0'
> String(tau);
'6.283185307179586'

We see from the second example that String also works on variables.

This method of converting to strings dovetails nicely with going the opposite direction, which uses the Number object directly:

> Number("6.283185307179586");
6.283185307179586
> String(Number("6.283185307179586"));
'6.283185307179586'
> Number('1.24e6')
1240000

We see in the final line that JavaScript supports scientific notation:

> 1.24e6
1240000

4.1.3 Exercises

  1. See if you can guess the return value of String(Number('1.24e6')). Confirm using the Node REPL.

  2. Like most programming languages, JavaScript lacks support for imaginary numbers, i.e., numbers that are a real multiple of the imaginary unit i (satisfying the equation i2 = –1, sometimes written as i=1). What is the JavaScript value of the square root of –1? By guessing or Googling, figure out what this value stands for. What is its boolean value?

4.2 Dates

Another frequently used built-in object is Date, which represents a single moment in time.

The Date object gives us our first chance to use the new function, a so-called constructor function that is the standard JavaScript way to create a new object. So far, we’ve been able to rely on “literal constructors” like quotes and square brackets, but we can also define things like strings and arrays using new:

> let s = new String("A man, a plan, a canal—Panama!");
> s;
[String: 'A man, a plan, a canal—Panama!']
> s.split(",  ");
[ 'A man', 'a plan', 'a canal—Panama!' ]

and

> let a = new Array();
> a.push(3);
1
> a.push(4);
2
> a.push("hello, world!");
3
> a;
[ 3, 4, 'hello, world!' ]
> a.pop();
'hello, world!'

Unlike strings and arrays, dates have no literal constructor, so we have to use new in this case:

> let now = new Date();
> now;
2022-03-16T 19:22:13.673Z
> let moonLanding = new Date("July 20, 1969 20:18");
> now - moonLanding;
1661616253673

The result here is the number of milliseconds since the day and time of the first Moon landing (Figure 4.2).4 (Your results, of course, will vary, because time marches on, and your value for new Date() will differ.)

4. Image courtesy of Castleski/Shutterstock.

images

Figure 4.2: Buzz Aldrin and Neil Armstrong somehow got to the Moon (and back!) without JavaScript.

As with other JavaScript objects, Date objects respond to a variety of methods:

> now.getYear();        // Gives a weird answer
122
> now.getFullYear();    // This is what we want instead.
2022
> now.getMonth();
2
> now.getDay();
3

The first line here shows that sometimes the results of JavaScript methods are confusing, so it’s important to be wary and double-check the values by hand from time to time.

Things like the month and day are returned as indices, and like everything in JavaScript they are zero-offset. For example, month 0 is January, month 1 is February, month 2 is March, etc.

Even though the official international standard is that Monday is the first day, JavaScript follows the American convention of using Sunday instead. We can get the name of the day by making an array of strings for the days of the week, and then using getDay() as an index in the array with the square bracket notation (Section 3.1):

> let daysOfTheWeek = ["Sunday", "Monday", "Tuesday", "Wednesday",
                       "Thursday", "Friday", "Saturday"];
> daysOfTheWeek[now.getDay()];
'Wednesday'

Your results will vary, of course, unless you happen to be reading this on a Wednesday.

As a final exercise, let’s update our web page with an alert including the day of the week. The code appears in Listing 4.1, with the result as shown in Figure 4.3.

images

Figure 4.3: A greeting customized just for today.

Listing 4.1: Adding a greeting customized to the day of the week.
index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Learn Enough JavaScript</title>
    <meta charset="utf-8">
    <script>
      const daysOfTheWeek = ["Sunday", "Monday", "Tuesday", "Wednesday",
                             "Thursday", "Friday", "Saturday"];
      let now = new Date();
      let dayName = daysOfTheWeek[now.getDay()];
      alert(`Hello, world! Happy ${dayName}.`);
    </script>
  </head>
  <body>

  </body>
</html>

Note that Listing 4.1 uses const instead of let when defining daysOfTheWeek:

const daysOfTheWeek = ["Sunday", "Monday", "Tuesday", "Wednesday",
                       "Thursday", "Friday", "Saturday"];

Here const, which (as you can probably guess) is short for “constant”, gives us a way to indicate that the value of the variable won’t change.5 Some people even go so far as to use const in preference to let whenever possible. My preference is to use let as a default, and to use const as a signal that it’s especially important for the value not to change.

5. Technically, const creates an immutable binding—i.e., the name can’t change, but the value can. Mutating the contents of a variable created using const is a bad practice, though, and should be avoided to prevent confusion.

4.2.1 Exercises

  1. Create a new Date object by passing it a string for your birthday (including year). JavaScript supports a number of different formats, so it will probably work with whichever date format you prefer. Pretty cool, no?

  2. How many seconds after the Moon landing were you born? (Or maybe you were even born before the Moon landing—in which case, lucky you! I hope you got to watch it on TV.)

4.3 Regular Expressions

JavaScript has full support for regular expressions, often called regexes or regexps for short, which are a powerful mini-language for matching patterns in text. A full mastery of regular expressions is beyond the scope of this book (and perhaps beyond the scope of human ability), but the good news is that there are many resources available for learning about them incrementally. (Some such resources are mentioned in “Grepping” (https://www.learnenough.com/command-line-tutorial/inspecting_files) in Learn Enough Command Line to Be Dangerous (https://www.learnenough.com/command-line) and “Global find and replace” (https://www.learnenough.com/text-editor-tutorial/advanced_text_editing#sec-global_find_and_replace) in Learn Enough Text Editor to Be Dangerous (https://www.learnenough.com/text-editor).) The most important thing to know about is the general idea of regular expressions; you can fill in the details as you go along.

Regexes are notoriously terse and error-prone; as programmer Jamie Zawinski famously said:

Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.

Luckily, this situation is greatly ameliorated by web applications like regex101 (https://regex101.com/), which let us build up regexes interactively (Figure 4.4). Moreover, such resources typically include a quick reference to assist us in finding the code for matching particular patterns (Figure 4.5).

images

Figure 4.4: An online regex builder.

images

Figure 4.5: A close-up of the regex reference.

If you look carefully at Figure 4.4, you might be able to see the checkmark in the menu on the left indicating that “javascript” has been selected for the regex input format. This arranges to use the exact regular expression conventions we need in this tutorial. In practice, languages differ little in their implementation of regular expressions, but it’s wise to use the correct language-specific settings, and always to double-check when moving a regex to a different language.

Let’s take a look at some simple regex matches in JavaScript. Our examples will draw on both the regex methods and string methods specialized for regexes. (The latter are often more convenient in practice.)

4.3.1 Regex Methods

A basic regex consists of a sequence of characters that matches a particular pattern. We can create a new regex using the new function (Section 4.2) on the RegExp object. For example, here’s a regex that matches standard American ZIP codes (Figure 4.6),6 consisting of five digits in a row:

6. Image courtesy of 4kclips/123RF.

> let zipCode = new RegExp("\d{5}");
images

Figure 4.6: 90210 is one of the most expensive ZIP codes in America.

Here d represents any digit (0–9), and the first backslash is needed to escape the second backslash to get a literal backslash in the string. (We’ll see how to avoid this inconvenient requirement using a literal regex constructor in Section 4.3.2.) Meanwhile, {5} says to match exactly five digits in a row.

If you use regular expressions a lot, eventually you’ll memorize many of these rules, but you can always look them up in a quick reference (Figure 4.5).

Now let’s see how to tell if a string matches a regex. Regular expressions come with an exec method that “executes” the regex on a string:

> let result = zipCode.exec("Beverly Hills 90210");
> result;
[ '90210', index: 14, input: 'Beverly Hills 90210' ]

The result here includes the matching string, the index number where the match starts, and the original input.

I don’t like the format of the result above, mainly because the output is a weird and confusing pseudo-array that seemingly has three elements but in fact has length 1:

> result.length
1

4.3.2 String Methods

A more convenient way to make regex matches is to use string methods. There’s also a literal regex constructor syntax that’s more convenient for most purposes.

As we learned in Section 4.2, some JavaScript objects need to be created using new, while others have optional literal constructors, such as quotes for making strings and square brackets for making arrays. Regexes support just such a literal constructor syntax, namely, patterns inside forward slashes:

> zipCode = /d{5}/;
/d{5}/

Note that, unlike in the case of the named RegExp constructor in Section 4.3.1, when using the literal constructor we don’t have to escape the d with an extra backslash.

Now let’s build up a longer string with multiple ZIP codes (Figure 4.7):7

7. Image courtesy of kitleong/123RF.

images

Figure 4.7: 91125 is a dedicated ZIP code for the campus of the California Institute of Technology (Caltech).

> let s = "Beverly Hills 90210 was a '90s TV show set in Los Angeles.";
> s += " 91125 is another ZIP code in the Los Angeles area."
'Beverly Hills 90210 was a '90s TV show set in Los Angeles. 91125 is another
ZIP code in the Los Angeles area.'

You should be able to use your technical sophistication (Box 1.1) to infer what the += operator does here if you haven’t seen it before (which might involve doing a quick Google search).

To find out whether the string matches the regex, we can use the string match method:

> s.match(zipCode);
[ '90210',
  index: 14,
  input: 'Beverly Hills 90210 was a '90s TV show set in Los Angeles. 91125 is
   another ZIP code in the Los Angeles area.' ]

The result is the same weird pseudo-array we saw in Section 4.3.1, but at least it gives the same result when run a second time:

> s.match(zipCode);
[ '90210',
  index: 14,
  input: 'Beverly Hills 90210 was a '90s TV show set in Los Angeles. 91125 is
   another ZIP code in the Los Angeles area.' ]

The match method is especially useful in conditionals; recalling the “bang bang” notation from Section 2.4.2, we can evaluate the match in a boolean context:

> !!s.match(zipCode);
true

Thus, we can do things like this:

> if (s.match(zipCode)) {
    "Looks like there's at least one ZIP code in the string!";
  }
'Looks like there's at least one ZIP code in the string!'

Even better, there’s a common technique for matching all occurrences of a regular expression using the “global flag” g after the second slash:

> zipCode = /d{5}/g;        // Use 'g' to set the 'global' flag.
/d{5}/g

The resulting output is pleasantly intuitive:

> s.match(zipCode);
[ '90210', '91125' ]

The result here is simply an array of the ZIP codes detected in the string, suitable for joining (Section 3.4.3) or iterating (Section 3.5 and Section 5.4).

Our final example of regexes combines the power of pattern matching with the split method we saw in Section 3.1. In that section, we split on spaces, like this:

> "ant bat cat duck".split("  ");
[ 'ant', 'bat', 'cat', 'duck' ]

We can obtain the same result in a more robust way by splitting on whitespace, which consists of spaces, tabs (indicated with ), and newlines (indicated with ).

Consulting the quick reference (Figure 4.5), we find that the regex for whitespace is s, and the way to indicate “one or more” is the plus sign +. Thus, we can split on whitespace as follows:

> "ant bat cat duck".split(/s+/);
[ 'ant', 'bat', 'cat', 'duck' ]

The reason this is so nice is that now we can get the same result if the strings are separated by multiple spaces, tabs, newlines, etc.:8

8. This pattern is so useful that it’s the default behavior for split in some languages (notably Ruby), so that "ant bat cat".split is ["ant", "bat", "cat"].

> "ant bat	cat
duck".split(/s+/);
[ 'ant', 'bat', 'cat', 'duck' ]

We also see here the value of the literal constructor: Especially when using short regexes, there’s no need to define an intermediate variable; instead, we can use the literal regex directly.

4.3.3 Exercises

  1. Write a regex that matches the extended-format ZIP code consisting of five digits, a hyphen, and a four-digit extension (such as 10118-0110). Confirm that it works using String#match and the caption in Figure 4.8.9

    9. Image courtesy of jordi2r/123RF.

    images

    Figure 4.8: ZIP code 10118-0110 (the Empire State Building).

  2. Write a regex that splits only on newlines. Such regexes are useful for splitting a block of text into separate lines. In particular, test your regex by pasting the poem in Listing 4.2 into the console and using sonnet.split(/your regex/). What is the length of the resulting array?

Listing 4.2: Some text with newlines.

> const sonnet = `Let me not to the marriage of true minds
Admit impediments. Love is not love
Which alters when it alteration finds,
Or bends with the remover to remove.
O no, it is an ever-fixed mark
That looks on tempests and is never shaken;
It is the star to every wand'ring bark,
Whose worth's unknown, although his height be taken.
Love's not time's fool, though rosy lips and cheeks
Within his bending sickle's compass come:
Love alters not with his brief hours and weeks,
But bears it out even to the edge of doom.
   If this be error and upon me proved,
   I never writ, nor no man ever loved.`;

4.4 Plain Objects

The word object is used in various contexts in JavaScript, usually referring to the abstract idea of a collection of data (properties) and functions (methods). As noted in Section 2.4, (almost) everything in JavaScript is an object, and we’ll see in Chapter 7 how to define objects that parallel built-in objects like String, Array, and RegExp. In this section, we’ll focus on plain objects, which are simpler to define than the more general objects we’ve encountered so far.

In general, objects in JavaScript can be dizzyingly complex, but in their simplest incarnation they work much like hashes (also called associative arrays) in other languages. You can think of them as being like regular arrays but with strings rather than integers as indices. Each element is thus a pair of values: a string (the key) and an element of any type (the value). These elements are also known as key–value pairs.

As a simple example, let’s create an object to store the first and last names of a user, such as we might have in a web application:

> let user = {};                     // {} is an empty Object.
{}
> user["firstName"] = "Michael";     // Key "firstName", value "Michael"
'Michael'
> user["lastName"] = "Hartl";        // Key "lastName", value "Hartl"
'Hartl'

As you can see, an empty Object is represented by curly braces, and we can assign values using the same square bracket syntax as for arrays. We can retrieve values in the same way:

> user["firstName"];      // Element access is like arrays
'Michael'
> user["lastName"];
'Hartl'

The keys in our object are nothing other than the properties we first met in Section 2.4, and as such they can also be accessed using the dot notation we saw with, e.g., string.length:

> user.firstName;       // Element access using the dot notation
'Michael'
> user.lastName;
'Hartl'

Deciding which syntax to use is a matter of context and style. Note that in either case an undefined key/property name simply returns undefined:

> user["dude"];
undefined
> user.dude;
undefined
> !!user.dude
false

The last line here is a reminder that undefined is false in a boolean context, which you may recall if you solved the corresponding exercise in Section 3.2.1.

Finally, we can simply display or define the full object, thereby showing the key–value pairs (Listing 4.3).

Listing 4.3: A literal representation of an object.

> user;
{ firstName: 'Michael', lastName: 'Hartl' }
> let otherUser = { firstName: 'Foo', lastName: 'Bar' };
> otherUser["firstName"];
'Foo'
> otherUser["lastName"];
'Bar'

4.4.1 Exercise

  1. Show that new Object() also works to create a new empty object. What happens if you give the object constructor an argument equal to the output of Listing 4.3?

4.5 Application: Unique Words

Let’s apply plain objects to a challenging exercise, consisting of our longest program so far. Our task is to extract all of the unique words in a fairly long piece of text, and count how many times each word appears.

Because the sequence of commands is rather extensive, our main tool will be a JavaScript file (Section 1.4), executed using the node command. (We’re not going to make it a self-contained shell script as in Section 1.5 because we don’t intend this to be a general-purpose utility program.) At each stage, I suggest using a Node REPL to execute the code interactively if you have any question about the effects of a given command.

Let’s start by creating our file:

$ touch count.js

Now fill it with a const containing the text, which we’ll choose to be Shakespeare’s Sonnet 11610 (Figure 4.9),11 as borrowed from Listing 4.2 and shown again in Listing 4.4.

10. Note that in the original pronunciation used in Shakespeare’s time, words like “love” and “remove” rhymed, as did “come” and “doom”.

11. Image courtesy of psychoshadowmaker/123RF.

images

Figure 4.9: Sonnet 116 compares love’s constancy to the guide star for a wandering bark (ship).

Listing 4.4: Adding some text as a const.
count.js

const sonnet = `Let me not to the marriage of true minds
Admit impediments. Love is not love
Which alters when it alteration finds,
Or bends with the remover to remove.
O no, it is an ever-fixed mark
That looks on tempests and is never shaken;
It is the star to every wand'ring bark,
Whose worth's unknown, although his height be taken.
Love's not time's fool, though rosy lips and cheeks
Within his bending sickle's compass come:
Love alters not with his brief hours and weeks,
But bears it out even to the edge of doom.
   If this be error and upon me proved,
   I never writ, nor no man ever loved.`;

Note that Listing 4.4 uses the backtick syntax (Section 2.2.1), which (as it turns out) allows us to break text across lines (unlike regular quotes).12 Note: Because this syntax is relatively new, some text editors (notably some versions of Sublime Text) might need to be configured to highlight it properly.

12. I had to Google around to learn how to do this.

Next, we’ll initialize our object, which we’ll call uniques because it will have an entry for each unique word in the text:

let uniques = {};

For the purposes of this exercise, we’ll define a “word” as a run of one or more word characters (i.e., letters or numbers, though there are none of the latter in the present text). This match can be accomplished with a regular expression (Section 4.3), which includes a pattern (w) for exactly this case (Figure 4.5):

let words = sonnet.match(/w+/g);

This uses the “global” g flag and the match method from Section 4.3.2 to return an array of all the strings that match “one or more word characters in a row”. (Extending this pattern to include apostrophes (so that it matches, e.g., “wand’ring” as well) is left as an exercise (Section 4.5.2).)

At this point, the file should look like Listing 4.5.

Listing 4.5: Adding an object and the matching words.
count.js

const sonnet = `Let me not to the marriage of true minds
Admit impediments. Love is not love
Which alters when it alteration finds,
Or bends with the remover to remove.
O no, it is an ever-fixed mark
That looks on tempests and is never shaken;
It is the star to every wand'ring bark,
Whose worth's unknown, although his height be taken.
Love's not time's fool, though rosy lips and cheeks
Within his bending sickle's compass come:
Love alters not with his brief hours and weeks,
But bears it out even to the edge of doom.
   If this be error and upon me proved,
   I never writ, nor no man ever loved.`;

let uniques = {};
let words = sonnet.match(/w+/g);

Now for the heart of our program. We’re going to loop through the words array (Section 3.5) and do the following:

  1. If the word already has an entry in the uniques object, increment its count by 1.

  2. If the word doesn’t have an entry yet in uniques, initialize it to 1.

The result, using the += operator we met briefly in Section 4.3.2, looks like this:

for (let i = 0; i < words.length; i++) {
  let word = words[i];
  if (uniques[word]) {
    uniques[word] += 1;
  } else {
    uniques[word] = 1;
  }
}

Among other things, we see here the power of the bracket access notation, as there would be no way to accomplish this same task using the dot syntax. Note also that we’re relying on uniques[word] being undefined (false in a boolean context) if word isn’t yet a valid key.

Finally, we’ll print out the result to the terminal:

console.log(uniques)

The full program (with added comments) appears as in Listing 4.6.

Listing 4.6: A program to count words in text.
count.js

const sonnet = `Let me not to the marriage of true minds
Admit impediments. Love is not love
Which alters when it alteration finds,
Or bends with the remover to remove.
O no, it is an ever-fixed mark
That looks on tempests and is never shaken;
It is the star to every wand'ring bark,
Whose worth's unknown, although his height be taken.
Love's not time's fool, though rosy lips and cheeks
Within his bending sickle's compass come:
Love alters not with his brief hours and weeks,
But bears it out even to the edge of doom.
   If this be error and upon me proved,
   I never writ, nor no man ever loved.`;

// Unique words
let uniques = {};
// All words in the text
let words = sonnet.match(/w+/g);
// Iterate through `words` and build up an associative array of unique words.
for (let i = 0; i < words.length; i++) {
  let word = words[i];
  if (uniques[word]) {
    uniques[word] += 1;
  } else {
    uniques[word] = 1;
  }
}

console.log(uniques)

It’s worth noting that, even in a relatively short program like Listing 4.6, it can be tricky to get all the braces, parentheses, etc., to match up. A good text editor can help; for example, when the cursor is next to a closing curly brace, Atom displays subtle bars under each member of the opening/closing pair (Figure 4.10).

images

Figure 4.10: Text editors can help immensely in matching up curly braces, etc.

The result of running count.js in the terminal looks something like this:

$ node count.js
{ Let: 1,
  me: 2,
  not: 4,
  to: 4,
  the: 4,
  marriage: 1,
  .
  .
  .
  upon: 1,
  proved: 1,
  I: 1,
  writ: 1,
  nor: 1,
  man: 1,
  loved: 1 }

4.5.1 Map

Although native JavaScript objects can be used as hashes/associative arrays (as we’ve done above), they do have their weaknesses, such as slower performance and limited support for extracting keys and values. JavaScript comes with a dedicated Map object to address these limitations, with set and get methods for setting and getting values using keys:

> let uniques = new Map();
> uniques.set("loved", 0);
Map { 'loved' => 0 }
> let currentValue = uniques.get("loved");
> uniques.set("loved", currentValue + 1);
Map { 'loved' => 1 }

Combining the techniques shown above to rewrite the count.js program is left as an exercise (Section 4.5.2).

4.5.2 Exercises

  1. Extend the regex used in Listing 4.6 to include an apostrophe, so it matches, e.g., “wand’ring”. Hint: Combine the first reference regex at regex101 (Figure 4.11) with w, an apostrophe, and the plus operator +.

    images

    Figure 4.11: An exercise hint.

  2. Rewrite Listing 4.6 using Map (Section 6.1) instead of native JavaScript objects.

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

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