O'Reilly logo

JavaScript Cookbook, 2nd Edition by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. The JavaScript Not-So-Simple Building Blocks

Most JavaScript functionality comes to us via a very basic set of objects and data types. The functionality associated with strings, numbers, and booleans is provided via the String, Number, and Boolean data types. Other fundamental functionality, including regular expressions, dates, and necessary mathematical operations, are provided via the built-in RegExp, Date, and Math objects, respectively.

The fundamental building blocks of JavaScript have changed over time, but their core functionality remains the same. In this chapter, I’m focusing less on the syntax associated with the objects, and more on understanding their place in JavaScript.

Note

A good, introductory overview of the JavaScript standard built-in objects can be found in the Mozilla Developer Network JavaScript Reference.

Differentiating Between a JavaScript Object, Primitive, and Literal

Problem

People toss around terms like object, primitive, and literal. What is the difference between the three, and how can you tell which is which?

Solution

A JavaScript literal represents a value of a specific type, such as a quoted string (String), floating-point number (Number), or boolean (Boolean):

"this is a string"
1.45
true

A JavaScript primitive is an instance of a particular data type, and there are five such in the language: String, Number, Boolean, null, and undefined. The following are examples of JavaScript primitives:

"this is a string"
null

Of the primitive data types, three have complementary constructor objects: String, Number, and Boolean. These objects provide access to the built-in properties and methods that allow us to do more than simple assignment and subsequent access:

var str1 = "this is a string";
console.log(str1.length); // using String object's length property

Note

Many of the examples in this book use the console.log() function to display JavaScript results. The Console Is Your Friend provides a quick how-to on accessing the JavaScript console in modern browers, and Appendix A also provides directions for setting up your environment and running the code snippets found in the solutions.

Discussion

It may seem as if we’re working with simple strings or numbers when we declare a variable:

var str1 = "this is a simple string";

However, we’re actually creating doorways into an extensive set of functionality. Without reliance on JavaScript objects, we can assign a string, number, or boolean value to a variable and then access it at a later time. However, if we want to do more with the variable, we’ll need to use the data type’s complementary JavaScript object and its properties.

As an example, if we want to see the length of a string, we’ll access the String object’s length property:

var str1 = "this is a simple string";
console.log(str1.length); // prints out 23 to browser console

Behind the scenes, when the code accesses a String object’s property on the literal, a new String object is created and its value is set to the value of the string contained in the variable. The length property is accessed and printed out, and the newly created String object is discarded.

Note

JavaScript engines don’t have to actually create an object to wrap the primitive when you access object properties; they only have to emulate this type behavior.

There are exactly five primitive data types in JavaScript: string, number, boolean, null, and undefined. Only the string, number, and boolean data types have complementary constructor objects. The actual representation of strings, floating-point numbers, integers, and booleans are literals:

var str1 = "this is a simple string"; // the quoted string is the literal

var num1 = 1.45; // the value of 1.45 is the literal

var answer = true; // the values of true and false are boolean literals

We can create primitive boolean, string, and number variables either by using a literal representation or using the object without using the new operator:

var str1 = String("this is a simple string"); // primitive string

var num1 = Number(1.45); // primitive number

var bool1 = Boolean(true); // primitive boolean

To deliberately instantiate an object, use the new operator:

var str2 = new String("this is a simple string"); // String object instance

var num2 = new Number(1.45); // Number object instance

var bool2 = new Boolean(true); // primitive boolean

You can quickly tell the difference between a primitive and an object instance when you compare an object instance to a literal value using strict equality. For example, running the following code in a browser:

var str1 = String("string");
var num1 = Number(1.45);
var bool1 = Boolean(true);

if (str1 === "string") {
  console.log('equal');
}

if (num1 === 1.45) {
  console.log('equal');
}

if (bool1 === true) {
  console.log('equal');
}

var str2 = new String("string");
var num2 = new Number(1.45);
var bool2 = new Boolean(true);

if (str2 === "string") {
  console.log('equal');
} else {
  console.log('not equal');
}

if (num2 === 1.45) {
  console.log('equal');
} else {
  console.log('not equal');
}

if (bool2 === true) {
  console.log('equal');
} else {
  console.log('not equal');
}

Results in the following print outs to the console:

equal
equal
equal
not equal
not equal
not equal

The primitive variables (those not created with new) are strictly equal to the literals, while the object instances are not. Why are the primitive variables strictly equal to the literals? Because primitives are compared by value, and values are literals.

For the most part, JavaScript developers don’t directly create object instances for the three primitive data types. Developers just want a number, boolean, or string variable to act like a number, boolean, or string, rather than an object; we don’t need the enhanced functionality of the object. More importantly, when developers use strict equality or type checking in the code, they want a variable to match their expectations of data type, rather than be defined as “object”:

var num1 = 1.45;

var num2 = new Number(1.45);

console.log(typeof num1); // prints out number
console.log(typeof num2); // prints out object

Code validators, such as JSHint, output a warning if you instantiate a primitive data type object directly for just this reason.

See Also

Checking for an Existing, Nonempty String has a more detailed look at the strict equality operators, as compared to the standard equality operators.

Extracting a List from a String

Problem

You have a string with several sentences, one of which includes a list of items. The list begins with a colon (:) and ends with a period (.), and each item is separated by a comma. You want to extract just the list.

Before:

This is a list of items: cherries, limes, oranges, apples.

After:

['cherries','limes','oranges','apples']

Solution

The solution requires two actions: extract out the string containing the list of items, and then convert this string into a list.

Use String’s indexOf() to locate the colon, and then use it again to find the first period following the colon. With these two locations, extract the string using String’s substring():

var sentence = 'This is one sentence. This is a sentence with a list of items:' +
'cherries, oranges, apples, bananas. That was the list of items.';
var start = sentence.indexOf(':');
var end = sentence.indexOf('.', start+1);

var listStr = sentence.substring(start+1, end);

Once you have the string consisting of the list items, use the String split() to break the string into an array:

var fruits = listStr.split(',');
console.log(fruits); // ['cherries', ' oranges', ' apples', ' bananas']

Discussion

The indexOf() method takes a search value, as first parameter, and an optional beginning index position, as second.

The list is delimited by a beginning colon character and an ending period. The indexOf() method is used without the second parameter in the first search, to find the colon. The method is used again but the colon’s position (plus 1) is used to modify the beginning location of the search for the period:

var end = sentence.indexOf('.',start+1);

If we didn’t modify the search for the ending period, we’d end up with the location of the first sentence’s period rather than the period for the sentence containing the list.

Once we have the beginning and ending location for the list, we’ll use the substring() method, passing in the two index values representing the beginning and ending positions of the string:

var listStr = sentence.substring(start+1, end);

The extracted string is:

cherries, oranges, apples, bananas

We’ll finish by using split() to split the list into its individual values:

var fruits = listStr.split(',') ; // ['cherries', ' oranges',
             ' apples', ' bananas']

There is another string extraction method, substr(), that begins extraction at an index position marking the start of the substring and passing in the length of the substring as the second parameter. We can easily find the length just by subtracting the beginning position of the string from the end position:

var listStr = sentence.substr(start+1, end-start);

var fruits = listStr.split(',');

See Also

Another way to extract the string is to use regular expressions and the RegExp object, covered beginning in Replacing Patterns with New Strings.

Advanced

The result of splitting the extracted string is an array of list items. However, the items come with artifacts (leading spaces) from sentence white space. In most applications, we’ll want to clean up the resulting array elements.

We’ll discuss the Array object in more detail in Chapter 2, but for now, we’ll use the Array forEach() method in addition to the String object’s trim() method to clean up the array:

fruits = listStr.split(',');

console.log(fruits); // [' cherries', ' oranges', ' apples', ' bananas']

fruits.forEach(function(elmnt,indx,arry) {
               arry[indx] = elmnt.trim();
});

console.log(fruits); // ['cherries', 'oranges', 'apples", "bananas"]

The forEach() method applies the function passed as parameter (the callback) to each array element. The callback function supports three arguments: the array element value, and optionally, the array element index and the array itself.

Another simpler approach is to pass a regular expression to the split() that trims the result before it’s returned:

var fruits = listStr.split(/\s*,\s*/);

Now the matching returned value is just the string without the surrounding white space.

Note

The forEach() method is also covered in Applying a Function Against Each Array Element. The code in this section mutates the array in place, which means it actually modifies the array as it’s traversed. Another nondestructive approach is to use the newer map() Array method, covered in Applying a Function to Every Element in an Array and Returning a New Array.

Extra: Simplifying the Code Using Chaining

The example code in this recipe is correct, but a little verbose. We can compress the code by using JavaScript’s method chaining, allowing us to attach one method call to the end of a previous method call if the object and methods allow it. In this case, we can chain the split() method directly to the substring() method:

var start = sentence.indexOf(":");
var end = sentence.indexOf(".", start+1);

var fruits = sentence.substring(start+1, end).split(",");

The code isn’t more accurate, but it uses less space and can be easier to read. I’ll cover method chaining in more detail in Chaining Your Object’s Methods.

Checking for an Existing, Nonempty String

Problem

You want to verify that a variable is defined, is a string, and is not empty.

Solution

The simplest solution when testing for a nonempty string is the following:

if (typeof unknownVariable === 'string' && unknownVariable.length > 0)

If the variable isn’t a string, the test will fail, and if the string’s length isn’t longer than zero (0), it will fail.

However, if you’re interested in testing for a string, regardless of whether it’s a String object or a string literal, you’ll need a different typeof test, as well as test to ensure the variable isn’t null:

if (((typeof unknownVariable != 'undefined' && unknownVariable) &&
    unknownVariable.length() > 0) &&
    typeof unknownVariable.valueOf() == 'string') ...

Discussion

You can use length to find out how long the string is and test whether the string variable is an empty string (zero length):

if (strFromFormElement.length == 0) // testing for empty string

However, when you’re working with strings and aren’t sure whether they’re set or not, you can’t just check their length, as you’ll get an undefined JavaScript error if the variable has never been set (or even declared). You have to combine the length test with another test for existence and this brings us to the typeof operator.

The JavaScript typeof operator returns the type of a variable. The list of possible returned values are:

  • number if the variable is a number
  • string if the variable is a string
  • boolean if the variable is a Boolean
  • function if the variable is a function
  • object if the variable is null, an array, or another JavaScript object
  • undefined if the variable is undefined

Combining the test for a string and a test on the string length ensures our app knows if the variable is a non-zero length string or not:

if (typeof unknownVariable == 'string' && unknownVariable.length > 0) ...

However, if you’re looking for a nonempty string regardless of whether the string is a literal or an object, than things get a little more interesting. A string that’s created using the String constructor:

var str = new String('test');

has a typeof value equal to object not string. We need a more sophisticated test.

First, we need a way to test whether a variable has been defined and isn’t null. The typeof can be used to ensure the variable isn’t undefined:

if (typeof unknownVariable != 'undefined')...

But it’s not sufficient, because null variables have a typeof value equal to object.

So the defined and not null test is changed to check to see if the variable is defined and isn’t null:

if (typeof unknownVariable != 'undefined' && unknownVariable) ...

Just listing the variable is sufficient to test whether it’s null or not.

We still don’t know, though, if the variable is a nonempty string. We’ll return the length test, which should allow us to test whether the variable is a string, and is not empty:

if ((typeof unknownVariable != 'undefined' && unknownVariable) &&
    unknownVariable.length > 0) ...

If the variable is a number, the test fails because a number doesn’t have a length. The String object and string literal variables succeed, because both support length. However, an array also succeeds, because the Array object also supports length.

To finish the test, turn to a little used method, valueOf(). The valueOf() method is available on all JavaScript objects, and returns the primitive (unwrapped) value of the object. For Number, String, and Boolean, valueOf() returns the primitive value. So if the variable is a String object, valueOf() returns a string literal. If the variable is already a string literal, applying the valueOf() method temporarily wraps it in a String object, which means the valueOf() method will still return a string literal.

Our finished test then becomes:

if(((typeof unknownVariable != "undefined" && unknownVariable) &&
     (typeof unknownVariable.valueOf() == "string")) &&

Now, the test functions without throwing an error regardless of the value and type of the unknown variable, and only succeeds with a nonempty string, regardless of whether the string is a string object or literal.

Note

Our use of valueOf() is limited. The method is primarily used by the JavaScript engine, itself, to handle conversions in instances when a primitive is expected and the engine is given an object.

The process is complex, and normally your application usually won’t have to be this extensive when testing a value. You’ll typically only need to test whether a variable has been set (typeof returns the correct data type), or find the length of a string in order to ensure it’s not an empty string.

Extra: Loose and Strict Equality Operators

I used loose equality (== and !=) in this section, but I use strict equality (=== and !==) elsewhere in the book. My use of both types of operators is not a typo.

Some folks (Douglas Crockford being the most outspoken) consider the loose equality operators (== and !=) to be evil, and discourage their use. The main reason many developers eschew loose equality operators is that they test primitive values rather than the variable object, in totality, and the results of the test can be unexpected.

For instance, the following code succeeds:

var str1 = new String('test');
if (str1 == 'test') { ...}

whereas this code fails:

var str1 = new String('test');
if (str1 === 'test') { ...}

The first code snippet succeeds because the string literal (test) and the primitive value the str1 variable contains are identical. The second code snippet fails the conditional test because the objects being compared are not equivalent, though they both share the same primitive value (test): the str1 variable is a String object, while the compared value is a string literal.

While results of this comparison are relatively intuitive, others are less so. In the following code snippet, a string is compared to a number, and the results may be unexpected:

var num = 0;
var str = '0';

console.log(num == str); // true
console.log(num === str); // false

In the Abstract Equality Comparison Algorithm, when a string is compared to a number, the loose equality comparison is treated as if the following occurs:

console.log(num === toNumber(str));

And therein lies the risk of loose equality: not understanding the implicit type conversion.

Sometimes, though, the very nature of the loose equality operator can be useful. For instance, the following code snippet demonstrates how the loose equality operator saves us time and code. The test to see if the variable is “bad” succeeds with standard equality regardless of whether the variable is undefined or null, where it wouldn’t succeed if strict equality had been used:

var str1;

if (str1 == null) {
  console.log('bad variable');
}

Rather than using the first typeof in the solution, I could shorten the test to the following and get the same result:

if ((unknownVariable != null && unknownVariable.length > 0) &&
     typeof unknownVariable.valueOf() == 'string') ...

Should you always use strict equality except in these rare instances? Just to ensure you don’t get unexpected results?

I’m going to buck the industry trend and say “No.” As long as you’re cognizant of how the equality operators work, and your code is such that you’re either only interested in primitive values or you want the object type coercion I just demonstrated, you can use loose equality operators in addition to strict equality.

Consider the following scenario: in a function, you’re testing to see if a counter is a certain value (100, for example). You’re expecting the counter, passed into the function, to be a number, but the developer who sent the value to your function passed it as a string.

When you perform the test, you are only interested in the value of the variable, not whether it’s a string or number. In this case, strict equality would fail, but not because the value isn’t what you’re testing for, but because the tested value and the function argument are different types. And the failure may be such that the developer using your function thinks that the application generating the value is in error, not that a type conversion hasn’t been made.

You don’t care in your function that the variable is a string and not a number. In this case, what you’re implicitly doing is converting the variable to what you expect and then doing the comparison. The following are equivalent:

if (counter == 100) ...

if (parseInt(counter) === 100) ...

Note

If the type is critically important, then a first test should be on the type and a relevant error generated. But this is what I mean by being cognizant of your code.

In a more realistic scenario, you may be working with a string, and you don’t care if the person who passed the string value to your function used a String constructor to create the string, or used a string literal—all you care about is the primitive value:

var str = 'test';
var str2 = new String('test');

doSomething(str);
doSomething(str2);
...

function doSomething (passedString) {

  if (passedString == 'test')
     ...
}

See Also

For more on the equality operators and their differences, as well as a view from the other side on the issue, I recommend JS101: Equality. The Mozilla Developer Network has a lovely, in-depth overview of the comparison operators and how they work in their documentation on comparison operators. And do check out the Abstract Equality Comparison Algorithm, directly.

Inserting Special Characters

Problem

You want to insert a special character, such as a line feed, into a string.

Solution

Use one of the escape sequences in the string. For instance, to include the copyright symbol in a block of text to be added to the page (shown in Figure 1-1), use the escape sequence \u00A9:

var resultString = "<p>This page \u00A9 Shelley Powers </p>";

// print out to page
 var blk = document.getElementById("result");
 blk.innerHTML = resultString;
Using an escape sequence to create the copyright symbol
Figure 1-1. Using an escape sequence to create the copyright symbol

Discussion

The escape sequences in JavaScript all begin with the backslash character, (\). This character signals the application processing the string that what follows is a sequence of characters that need special handling. Table 1-1 lists the other escape sequences.

Table 1-1. Escape sequences
SequenceCharacter

\'

Single quote

\"

Double quote

\\

Backslash

\b

Backspace

\f

Form feed

\n

Newline

\r

Carriage return

\t

Horizontal tab

\ddd

Octal sequence (3 digits: ddd)

\xdd

Hexadecimal sequence (2 digits: dd)

\udddd

Unicode sequence (4 hex digits: dddd)

The last three escape sequences in Table 1-1 are patterns, where providing different numeric values will result in differing escape sequences. The copyright symbol in the solution is an example of the Unicode sequence pattern.

The escape sequences listed in Table 1-1 can also be represented as a Unicode sequence. Unicode is a computing standard for consistent encoding, and a Unicode sequence is a specific pattern for a given character. For instance, the horizontal tab (\t), can also be represented as the Unicode escape sequence, \u0009. Of course, if the user agent disregards the special character, as browsers do with the horizontal tab, the use is moot.

One of the most common uses of escape sequences is to include double or single quotes within strings delimited by the same character:

var newString = 'You can\'t use single quotes ' +
                'in a string surrounded by single quotes.' +
                'Oh, wait a sec...yes you can.';

Replacing Patterns with New Strings

Problem

You want to replace all matched substrings with a new substring.

Solution

Use the String’s replace() method, with a regular expression:

var searchString = "Now is the time, this is the tame";
var re = /t\w{2}e/g;
var replacement = searchString.replace(re, "place");
console.log(replacement); // Now is the place, this is the place

Discussion

The solution also makes use of a global search. Using the global flag (g) with the regular expression in combination with the String replace() method will replace all instances of the matched text with the replacement string. If we didn’t use the global flag, only the first match would trigger a replacement.

The literal regular expression begins and ends with a slash (/). As an alternative, I could have used the built-in RegExp object:

var re = new RegExp('t\\w{2}e',"g");
var replacement = searchString.replace(re,"place");
console.log(p);

The difference is the surrounding slashes aren’t necessary when using RegExp, but the use of the backslash in the pattern has to be escaped. In addition, the global indicator is a second, optional argument to the RegExp constructor.

You can use a regular expression literal or a RegExp object instance interchangeably. The primary difference is that the RegExp constructor allows you to create the regular expression dynamically.

Extra: Regular Expression Quick Primer

Regular expressions are made up of characters that are used alone or in combination with special characters. For instance, the following is a regular expression for a pattern that matches against a string that contains the word technology and the word book, in that order, and separated by one or more whitespace characters:

var re = /technology\s+book/;

The backslash character (\) serves two purposes: either it’s used with a regular character, to designate that it’s a special character, or it’s used with a special character, such as the plus sign (+), to designate that the character should be treated literally. In this case, the backslash is used with s, which transforms the letter s to a special character designating a whitespace character (space, tab, line feed, or form feed). The \s special character is followed by the plus sign, \s+, which is a signal to match the preceding character (in this example, a whitespace character) one or more times. This regular expression would work with the following:

technology book

It would also work with the following:

technology     book

It would not work with the following, because there is no white space between the words:

technologybook

It doesn’t matter how much whitespace is between technology and book, because of the use of \s+. However, using the plus sign does require at least one whitespace character.

Table 1-2 shows the most commonly used special characters in JavaScript applications.

Table 1-2. Regular expression special characters
CharacterMatchesExample

^

Matches beginning of input

/^This/ matches This is…

$

Matches end of input

/end$/ matches This is the end

*

Matches zero or more times

/se*/ matches seeee as well as se

?

Matches zero or one time

/ap?/ matches apple and and

+

Matches one or more times

/ap+/ matches apple but not and

{n}

Matches exactly n times

/ap{2}/ matches apple but not apie

{n,}

Matches n or more times

/ap{2,}/ matches all p’s in apple and appple but not apie

{n,m}

Matches at least n, at most m times

/ap{2,4}/ matches four p’s in apppppple

.

Any character except newline

/a.e/ matches ape and axe

[…]

Any character within brackets

/a[px]e/ matches ape and axe but not ale

[^…]

Any character but those within brackets

/a[^px]/ matches ale but not axe or ape

\b

Matches on word boundary

/\bno/ matches the first no in nono

\B

Matches on nonword boundary

/\Bno/ matches the second no in nono

\d

Digits from 0 to 9

/\d{3}/ matches 123 in Now in 123

\D

Any nondigit character

/\D{2,4}/ matches Now ' in ‘Now in 123;

\w

Matches word character (letters, digits, underscores)

/\w/ matches j in javascript

\W

Matches any nonword character (not letters, digits, or underscores)

\/W/ matches % in 100%

\n

Matches a line feed

\s

A single whitespace character

\S

A single character that is not whitespace

\t

A tab

(x)

Capturing parentheses

Remembers the matched characters

Note

Regular expressions are powerful but can be tricky. I’m only covering them lightly in this book. If you want more in-depth coverage of regular expressions, I recommend the excellent Regular Expressions Cookbook by Jan Goyvaerts and Steven Levithan (O’Reilly).

See Also

Swapping Words in a String Using Capturing Parentheses shows variations of using regular expressions with the String replace method, including the use of capturing parenthesis.

Finding and Highlighting All Instances of a Pattern

Problem

You want to find all instances of a pattern within a string.

Solution

Use the RegExp exec method and the global flag (g) in a loop to locate all instances of a pattern, such as any word that begins with t and ends with e, with any number of characters in between:

var searchString = "Now is the time and this is the time and that is the time";
var pattern = /t\w*e/g;
var matchArray;

var str = "";

// check for pattern with regexp exec, if not null, process
while((matchArray = pattern.exec(searchString)) != null) {
  str+="at " + matchArray.index + " we found " + matchArray[0] + "\n";
}
console.log(str);

The results are:

at 7 we found the
at 11 we found time
at 28 we found the
at 32 we found time
at 49 we found the
at 53 we found time

Discussion

The RegExp exec() method executes the regular expression, returning null if a match is not found, or an object with information about the match, if found. Included in the returned array is the actual matched value, the index in the string where the match is found, any parenthetical substring matches, and the original string:

  • index: The index of the located match
  • input: The original input string
  • [0]: The matched value
  • [1],…,[n]+: Parenthesized substring matches, if any

The parentheses capture the matched values. Given a regular expression like that in the following code snippet:

var re = /a(p+).*(pie)/ig;
var result = re.exec("The apples in the apple pie are tart");
console.log(result);
console.log(result.index);
console.log(result.input);

the resulting output is:

["apples in the apple pie", "pp", "pie"]
4
"The apples in the apple pie are tart"

The array results contain the complete matched value at index zero (0), and the rest of the array entries are the parenthetical matches. The index is the index of the match, and the input is just a repeat of the string being matched. In the solution, the index where the match was found is printed out in addition to the matched value.

The solution also uses the global flag (g). This triggers the RegExp object to preserve the location of each match, and to begin the search after the previously discovered match. When used in a loop, we can find all instances where the pattern matches the string. In the solution, the following are printed out:

at 7 we found the
at 11 we found time
at 28 we found the
at 32 we found time
at 49 we found the
at 53 we found time

Both time and the match the pattern.

Let’s look at the nature of global searching in action. In Example 1-1, a web page is created with a textarea and an input text box for accessing both a search string and a pattern. The pattern is used to create a RegExp object, which is then applied against the string. A result string is built, consisting of both the unmatched text and the matched text, except the matched text is surrounded by a span element (with a CSS class used to highlight the text). The resulting string is then inserted into the page, using the innerHTML for a div element.

Example 1-1. Using exec and global flag to search and highlight all matches in a text string
<!DOCTYPE html>
<html>
<head>
<title>Searching for strings</title>
<style>
.found
{
  background-color: #ff0;
}
</style>

</head>
<body>
  <form id="textsearch">
    <textarea id="incoming" cols="150" rows="10">
    </textarea>
   <p>
     Search pattern: <input id="pattern" type="text" />
   </p>
  </form>
  <button id="searchSubmit">Search for pattern</button>
  <div id="searchResult"></div>

<script>

  document.getElementById("searchSubmit").onclick=function() {

    // get pattern
    var pattern = document.getElementById("pattern").value;
    var re = new RegExp(pattern,"g");

    // get string
    var searchString = document.getElementById("incoming").value;

    var matchArray;
    var resultString = "<pre>";
    var first=0;
    var last=0;

    // find each match
    while((matchArray = re.exec(searchString)) != null) {
      last = matchArray.index;

      // get all of string up to match, concatenate
      resultString += searchString.substring(first, last);

      // add matched, with class
      resultString += "<span class='found'>" + matchArray[0] + "</span>";
      first = re.lastIndex;
    }

    // finish off string
    resultString += searchString.substring(first,searchString.length);
    resultString += "</pre>";

    // insert into page
    document.getElementById("searchResult").innerHTML = resultString;
 }

</script>
</body>
</html>

Figure 1-2 shows the application in action on William Wordsworth’s poem, “The Kitten and the Falling Leaves” after a search for the following pattern:

lea(f|ves)

The bar (|) is a conditional test, and will match a word based on the value on either side of the bar. So leaf matches, as well as leaves, but not leap.

Application finding and highlighting all matched strings
Figure 1-2. Application finding and highlighting all matched strings

You can access the last index found through the RegExp’s lastIndex property. The lastIndex property is handy if you want to track both the first and last matches.

See Also

Replacing Patterns with New Strings describes another way to do a standard find-and-replace behavior, and Swapping Words in a String Using Capturing Parentheses provides a simpler approach to finding and highlighting text in a string.

Swapping Words in a String Using Capturing Parentheses

Problem

You want to accept an input string with first and last name, and swap the names so the last name is first.

Solution

Use capturing parentheses and a regular expression to find and remember the two names in the string, and reverse them:

var name = "Abe Lincoln";
var re = /^(\w+)\s(\w+)$/;
var newname = name.replace(re,"$2, $1");

Discussion

Capturing parentheses allow us to not only match specific patterns in a string, but to reference the matched substrings at a later time. The matched substrings are referenced numerically, from left to right, as represented by $1 and $2 in the replace() method.

In the solution, the regular expression matches two words separated by a space. Capturing parentheses were used with both words, so the first name is accessible using $1, the last name with $2.

The capturing parentheses aren’t the only special characters available with replace(). Table 1-3 shows the other special characters that can be used with regular expressions and String replace().

Table 1-3. String.replace special patterns
PatternPurpose

$$

Allows a literal dollar sign ($) in replacement

$&

Inserts matched substring

$`

Inserts portion of string before match

$'

Inserts portion of string after match

$n

Inserts n th captured parenthetical value when using RegExp

The second pattern, which reinserts the matched substring, can be used to provide a simplified version of the Example 1-1 application shown in Finding and Highlighting All Instances of a Pattern. That example found and provided markup and CSS to highlight the matched substring. It used a loop to find and replace all entries, but in Example 1-2 we’ll use replace() with the matched substring special pattern ($&).

Example 1-2. Using String.replace and special pattern to find and highlight text in a string
<!DOCTYPE html>
<html>
<head>
<title>Searching for strings</title>
<style>
.found
{
  background-color: #ff0;
}
</style>
</head>
<body>
  <form id="textsearch">
    <textarea id="incoming" cols="100" rows="10">
    </textarea>
    <p>
      Search pattern: <input id="pattern" type="text" />
    </p>
  </form>
  <button id="searchSubmit">Search for pattern</button>
  <div id="searchResult"></div>

<script>

  document.getElementById("searchSubmit").onclick=function() {

    // get pattern
    var pattern = document.getElementById("pattern").value;
    var re = new RegExp(pattern,"g");

    // get string
    var searchString = document.getElementById("incoming").value;

    // replace
    var resultString = searchString.replace(re,"<span class='found'>$&</span>");

    // insert into page
    document.getElementById("searchResult").innerHTML = resultString;
 }

</script>
</body>
</html>

This is a simpler alternative, but the result isn’t quite the same: the line feeds aren’t preserved with Example 1-2, but they are with Example 1-1.

The captured text can also be accessed via the RegExp object using the exec() method. Let’s return to the Swapping Words in a String Using Capturing Parentheses solution code, this time using exec():

var name = "Abe Lincoln";
var re = /^(\w+)\s(\w+)$/;
var result = re.exec(name);
var newname = result[2] + ", " + result[1];

This approach is handy if you want to access the capturing parentheses values, but without having to use them within a string replacement.

Replacing HTML Tags with Named Entities

Problem

You want to paste example markup into a web page, and escape the markup (i.e., have the angle brackets print out rather than have the contents parsed).

Solution

Use regular expressions to convert angle brackets (<>) into the named entities &lt; and &gt;:

var pieceOfHtml = "<p>This is a <span>paragraph</span></p>";
pieceOfHtml = pieceOfHtml.replace(/</g,"&lt;");
pieceOfHtml = pieceOfHtml.replace(/>/g,"&gt;");
console.log(pieceOfHtml);

Discussion

It’s not unusual to want to paste samples of markup into another web page. The only way to have the text printed out, as is, without having the browser parse it, is to convert all angle brackets into their equivalent named entities.

The process is simple with the use of regular expressions and the String replace method, as demonstrated in the solution. The key is to remember to use the global flag with the regular expression, to match all instances of the angle brackets.

Caution

Of course, if the regular expression finds the use of > or < in a mathematical or conditional expression, it will replace these, too.

Converting an ISO 8601 Formatted Date to a Date Object Acceptable Format

Problem

You need to convert an ISO 8601 formatted date string into values that can be used to create a new Date object instance.

Solution

Parse the ISO 8601 string into the individual date values, and use it to create a new JavaScript Date object instance:

var dtstr= "2014-3-04T19:35:32Z";

dtstr = dtstr.replace(/\D/g," ");
var dtcomps = dtstr.split(" ");

// modify month between 1 based ISO 8601 and zero based Date
dtcomps[1]--;

var convdt = new Date(Date.UTC.apply(null,dtcomps));

console.log(convdt.toString()); // Tue, 04 Mar 2014 19:35:32 GMT

Discussion

The ISO 8601 is an international standard that defines a representation for both dates and times. It’s not unusual for applications that provide APIs to require ISO 8601 formatting. It’s also not unusual for most dates to and from APIs to be in UTC, rather than local time.

The solution shows one variation of ISO 8601 formatting. The following demonstrate some others:

  • 2009
  • 2009-10
  • 2009-10-15
  • 2009-10-15T19:20
  • 2009-10-15T19:20:20
  • 2009-10-15T19:20:20.50

The values are year, month, date, then T to represent time, and hours, minutes, seconds, and fractions of sections. The time zone also needs to be indicated. If the date is in UTC, the time zone is represented by the letter Z, as shown in the solution:

2014-3-04T19:35:32Z

Otherwise, the time zone is represented as +hh:mm to represent a time zone ahead of UTC, and -hh:mm to represent a time zone behind UTC.

If you attempt to create a JavaScript Date with an ISO 8601 formatted string, you’ll get an invalid date error. Instead, you have to convert the string into values that can be used with the JavaScript Date.

The simplest way to parse an ISO 8601 formatted string is to use the String split() method. To facilitate using split(), all non-numeric characters are converted to one specific character. In the solution, the non-numeric characters are converted to a space:

dtstr = dtstr.replace(/\D/g, " ");

The ISO-formatted string would be converted to:

2014 03 04 19 35 32

ISO months are one-based values of 1 through 12. To use the month value in JavaScript Dates, the month needs to be adjusted by subtracting 1:

dtcomps[1]--;

Finally, the new Date is created. To maintain the UTC setting, the Date’s UTC() method is used to create the date in universal time, which is then passed to the Date constructor. Rather than listing out each and every single date value, the apply() method is used, with null as the first value, and all of the arguments as an array as the second:

var convdt = new Date(Date.UTC.apply(null,dtcomps));

The task gets more challenging when you have to account for the different ISO 8601 formats. Example 1-3 shows a JavaScript application that contains a more complex JavaScript function that converts from ISO 8601 to allowable Date values. The first test in the function ensures that the ISO 8601 format can be converted to a JavaScript Date. This means that, at a minimum, the formatted string must have a month, day, and year.

Example 1-3. Converting ISO 8601 formatted dates to JavaScript Dates
<!DOCTYPE html>
<html>
<head>
   <title>Converting ISO 8601 date</title>
</head>
<body>
  <form>
    <p>Datestring in ISO 8601 format: <input type="text" id="datestring" />
    </p>
  </form>
  <button id="dateSubmit">Convert Date</button>
  <div id="result"></div>

 <script type="text/javascript">
   document.getElementById("dateSubmit").onclick=function() {

     var dtstr = document.getElementById("datestring").value;
     var convdate = convertISO8601toDate(dtstr);
     document.getElementById("result").innerHTML=convdate;
   }

   function convertISO8601toDate(dtstr) {

     // replace anything but numbers by spaces
     dtstr = dtstr.replace(/\D/g," ");

     // trim any hanging white space
     dtstr = dtstr.replace(/\s+$/,"");

     // split on space
     var dtcomps = dtstr.split(" ");

     // not all ISO 8601 dates can convert, as is
     // unless month and date specified, invalid
     if (dtcomps.length < 3) return "invalid date";

     // if time not provided, set to zero
     if (dtcomps.length < 4) {
         dtcomps[3] = 0;
         dtcomps[4] = 0;
         dtcomps[5] = 0;
     }

     // modify month between 1 based ISO 8601 and zero based Date
     dtcomps[1]--;

     var convdt = new Date(Date.UTC.apply(null,dtcomps));

     return convdt.toUTCString();
   }
 </script>

</body>
</html>

Another test incorporated into Example 1-3 is whether a time is given. If there aren’t enough array elements to cover a time, then the hours, minutes, and seconds are set to zero when the UTC date is created.

There are other issues related to dates not covered in the application. For instance, if the ISO 8601 formatted string isn’t in UTC time, converting it to UTC can require additional code, both to parse the time zone and to adjust the date to incorporate the time zone.

Note

Eventually, you won’t need this special processing, because ECMAScript 5 includes support for ISO 8601 dates in methods such as Date parse(). However, implementation is still inconsistent across all major browsers—nonexistent in older browsers—so you’ll need these workarounds, or a shim, for now. See Using ES6 String Extras Without Leaving Users in the Dirt for more on using a shim.

Using Function Closures with Timers

Problem

You want to provide a function with a timer, but you want to add the function directly into the timer method call.

Solution

Use an anonymous function as first parameter to the setInterval() or setTimeout() method call:

intervalId=setInterval(
            function() {
              x+=5;
              var left = x + "px";
              document.getElementById("redbox").style.left=left;
            },100);

Discussion

Unlike the other material covered in this chapter, JavaScript timers don’t belong to any of the basic built-in objects. Instead, they’re part of the basic Web API (previously known as the Browser Object Model, or BOM). In the browser, they’re properties of the Window object, the browser’s global object, though we don’t need to specify window when accessing them. In Node.js, the two timer functions are part of the global object.

When you’re creating timers using setTimeout() and setInterval(), you can pass in a function variable as the first parameter:

function bing() {
  alert('Bing!');
}

setTimeout(bing, 3000);

However, you can also use an anonymous function, as demonstrated in the solution. This approach is more useful, because rather than have to clutter up the global space with a function just to use with the timer, you can embed the function directly. In addition, you can use a variable local to the scope of the enclosing function when you use an anonymous function.

Example 1-4 demonstrates an anonymous function within a setInterval() method call. The approach also demonstrates how the use of this function closure allows access to the parent function’s local variables within the timer method. In the example, clicking the red box starts the timer, and the box moves. Clicking the box again clears the timer, and the box stops. The position of the box is tracked in the x variable, which is within scope for the timer function, as it operates within the scope of the parent function.

Example 1-4. Using an anonymous function within a setInterval timer parameter
<!DOCTYPE html>
<head>
<title>interval and anonymous function</title>
<style>
#redbox
{
  position: absolute;
  left: 100px;
  top: 100px;
  width: 200px; height: 200px;
  background-color: red;
}
</style>
</head>
<body>
<div id="redbox"></div>

<script>
  var intervalId=null;

  document.getElementById('redbox').addEventListener('click',startBox,false);

  function startBox() {
    if (intervalId == null) {
       var x = 100;
       intervalId=setInterval(
            function() {
              x+=5;
              var left = x + "px";
              document.getElementById("redbox").style.left=left;
            },100);
    } else {
       clearInterval(intervalId);
       intervalId=null;
    }
  }
</script>

</body>

There’s no guarantee that the timer event fires when it is supposed to fire. Timers run on the same execution thread as all other user interface (UI) events, such as mouse-clicks. All events are queued and blocked, including the timer event, until its turn. So, if you have several events in the queue ahead of the timer, the actual time could differ—probably not enough to be noticeable to your application users, but a delay can happen.

See Also

John Resig offers an excellent discussion on how timers work, and especially the issues associated with event queues and single threads of execution.

Function closures are covered in more detail in Creating a Function That Remembers Its State. See function closures in timers in action in Tracking Elapsed Time.

Tracking Elapsed Time

Problem

You want to track the elapsed time between events.

Solution

Create a Date object when the first event occurs, a new Date object when the second event occurs, and subtract the first from the second. The difference is in milliseconds; to convert to seconds, divide by 1,000:

var firstDate = new Date();

setTimeout(function() {
  doEvent(firstDate);
}, 25000);

function doEvent() {
  var secondDate = new Date();
  var diff = secondDate - firstDate;
  console.log(diff); // approx. 25000
}

Discussion

Some arithmetic operators can be used with Date, but with interesting results. In the example, one Date instance can be subtracted from another, and the difference between the two is returned as milliseconds. However, if you add two dates together, the result is a string with the second Date instance concatenated to the first:

Thu Oct 08 2009 20:20:34 GMT-0500 (CST)Thu Oct 08 2009 20:20:31 GMT-0500 (CST)

If you divide the Date instances, the dates are converted to their millisecond value, and the result of dividing one by the other is returned. Multiplying two dates will return a very large millisecond result.

Note

Only the Date instance subtraction operator really makes sense, but it’s interesting to see what happens with arithmetic operators and the Date object.

Converting a Decimal to a Hexadecimal Value

Problem

You have a decimal value, and need to find its hexadecimal equivalent.

Solution

Use the Number toString() method:

var num = 255;

// displays ff, which is hexadecimal equivalent for 255
console.log(num.toString(16));

Discussion

By default, numbers in JavaScript are base 10, or decimal. However, they can also be converted to a different radix, including hexadecimal (16) and octal (8). Hexadecimal numbers begin with 0x (a zero followed by lowercase x), and octal numbers always begin with zero:

var octoNumber = 0255; // equivalent to 173 decimal
var hexaNumber = 0xad; // equivalent to 173 decimal

A decimal number can be converted to another radix, in a range from 2 to 36:

var decNum = 55;
var octNum = decNum.toString(8); // value of 67 octal
var hexNum = decNum.toString(16); // value of 37 hexadecimal
var binNum = decNum.toString(2); // value of 110111 binary

To complete the octal and hexadecimal presentation, you’ll need to concatenate the zero to the octal, and the 0x to the hexadecimal value.

Although decimals can be converted to any base number (between a range of 2 to 36), only the octal, hexadecimal, and decimal numbers can be manipulated, directly as numbers. In addition, when using JavaScript strict mode, only decimal and hexadecimal literals are supported, as octal integers are no longer supported in JavaScript.

Extra: Speaking of Strict Mode

Strict mode is an ECMAScript 5 addition that signals the use of a more restricted version of the JavaScript language. Strict mode can be implemented for an entire script or only for a function. Triggering is simple:

'use strict';

or:

"use strict";

This code should be the first line in your script block or function.

When strict mode is engaged, a mistake that would normally be ignored now generates an error. What kind of mistake?

  • Typos in variable names in assignment throw an error.
  • Assignments that would normally fail quietly now throw an error.
  • Attempting to delete an undeletable property fails.
  • Using nonunique property names.
  • Using nonunique function parameter names.

Strict mode also triggers other requirements:

  • Octals aren’t supported in strict mode.
  • The eval() statement is limited, and with is not supported.
  • When constructing a new object, new is required for this to function correctly.

Bottom line: strict mode helps eliminate unexpected and unexplainable results.

Summing All Numbers in a Table Column

Problem

You want to sum all numbers in a table column.

Solution

Traverse the table column containing numeric string values, convert to numbers, and sum the numbers:

var sum = 0;

// use querySelector to find all second table cells
var cells = document.querySelectorAll("td:nth-of-type(2)");

for (var i = 0; i < cells.length; i++) {
  sum+=parseFloat(cells[i].firstChild.data);
}

Discussion

The global functions parseInt() and parseFloat() convert strings to numbers, but parseFloat() is more adaptable when it comes to handling numbers in an HTML table. Unless you’re absolutely certain all of the numbers will be integers, parseFloat() can work with both integers and floating-point numbers.

As you traverse the HTML table and convert the table entries to numbers, sum the results. Once you have the sum, you can use it in a database update, print it to the page, or pop up a message box, as the solution demonstrates.

You can also add a sum row to the HTML table. Example 1-5 demonstrates how to convert and sum up numeric values in an HTML table, and then how to insert a table row with this sum, at the end. The code uses document.querySelectorAll(), which uses a different variation on the CSS selector, td + td, to access the data this time. This selector finds all table cells that are preceded by another table cell.

Example 1-5. Converting table values to numbers and summing the results
<!DOCTYPE html>
<html>
<head>
<title>Accessing numbers in table</title>
</head>
<body>
<table id="table1">
   <tr>
      <td>Washington</td><td>145</td>
   </tr>
   <tr>
      <td>Oregon</td><td>233</td>
   </tr>
   <tr>
      <td>Missouri</td><td>833</td>
   </tr>
</table>
<script type="text/javascript">

   var sum = 0;

   // use querySelector to find all second table cells
   var cells = document.querySelectorAll("td + td");

   for (var i = 0; i < cells.length; i++)
      sum+=parseFloat(cells[i].firstChild.data);

   // now add sum to end of table
   var newRow = document.createElement("tr");

   // first cell
   var firstCell = document.createElement("td");
   var firstCellText = document.createTextNode("Sum:");
   firstCell.appendChild(firstCellText);
   newRow.appendChild(firstCell);

   // second cell with sum
   var secondCell = document.createElement("td");
   var secondCellText = document.createTextNode(sum);
   secondCell.appendChild(secondCellText);
   newRow.appendChild(secondCell);

   // add row to table
   document.getElementById("table1").appendChild(newRow);

</script>
</body>
</html>

Being able to provide a sum or other operation on table data is helpful if you’re working with dynamic updates via an Ajax operation, such as accessing rows of data from a database. The Ajax operation may not be able to provide summary data, or you may not want to provide summary data until a web page reader chooses to do so. The users may want to manipulate the table results, and then push a button to perform the summing operation.

Adding rows to a table is simple, as long as you remember the steps:

  1. Create a new table row using document.createElement("tr").
  2. Create each table row cell using document.createElement("td").
  3. Create each table row cell’s data using document.createTextNode(), passing in the text of the node (including numbers, which are automatically converted to a string).
  4. Append the text node to the table cell.
  5. Append the table cell to the table row.
  6. Append the table row to the table. Rinse, repeat.

If you perform this operation frequently, you can create functions for these operations, and package them into JavaScript libraries that you can reuse. Also, many of the available JavaScript libraries can do much of this work for you.

See Also

Wonder why I’m not using forEach() with the results of the query? That’s because the querySelectorAll() returns a NodeList, not an array, and forEach() is an Array method. But there is a workaround, covered in Traversing the Results from querySelectorAll() with forEach() and call().

Extra: Modularization of Globals

The parseFloat() and parseInt() methods are global methods. As part of a growing effort to modularize JavaScript, both methods are now attached to the Number object, as new static methods, in ECMAScript 6:

var num = Number.parseInt('123');

The motive is good, but at the time this book was written, only Firefox supported the Number methods.

Converting Between Degrees and Radians

Problem

You have an angle in degrees. To use the value in the Math object’s trigonometric functions, you need to convert the degrees to radians.

Solution

To convert degrees to radians, multiply the value by (Math.PI / 180):

var radians = degrees * (Math.PI / 180);

To convert radians to degrees, multiply the value by (180 / Math.PI):

var degrees = radians * (180 / Math.PI);

Discussion

All Math trigonometric methods (sin(), cos(), tin(), asin(), acos(), atan(), and atan2()), take values in radians, and return radians as a result. Yet it’s not unusual for people to provide values in degrees rather than radians, as degrees are the more familiar unit of measure. The functionality covered in the solution provides the conversion between the two units.

Find the Radius and Center of a Circle to Fit Within a Page Element

Problem

Given the width and height of a page element, you need to find the center and radius of the largest circle that fits within that page element.

Solution

Find the smaller of the width and height; divide this by 2 to find the radius:

var circleRadius = Math.min(elementWidth, elementHeight) / 2;

Given the page element’s width and height, find the center by dividing both by 2:

var x = elementWidth / 2;
var y = elementHeight / 2;

Discussion

Working with graphics requires us to do things such as finding the center of an element, or finding the radius of the largest circle that will fit into a rectangle (or largest rectangle that can fit in a circle).

Example 1-6 demonstrates both of the solution calculations, modifying an SVG circle contained within an HTML document so that the circle fits within the div element that surrounds it.

Example 1-6. Fitting a SVG circle into a div element
<!DOCTYPE html>
<html>
<head>
<title>Using Math method to fit a circle</title>
<style type="text/css">

#elem
{
   width: 600px;
   height: 400px;
   border: 1px solid black;
}
</style>
<script type="text/javascript">

window.onload = window.onresize = function() {
  var box = document.getElementById("elem");
  var style = window.getComputedStyle(box,null);

  var height = parseInt(style.getPropertyValue("height"));
  var width = parseInt(style.getPropertyValue("width"));

  var x = width / 2;
  var y = height / 2;

  var circleRadius = Math.min(width,height) / 2;

  var circ = document.getElementById("circ");
  circ.setAttribute("r",circleRadius);
  circ.setAttribute("cx",x);
  circ.setAttribute("cy",y);
}

</script>

</head>
<body>
<div id="elem">
   <svg width="100%" height="100%">
      <circle id="circ" width="10" height="10" r="10" fill="red" />
   </svg>

</div>
</body>

Figure 1-3 shows the page once it’s loaded. There are techniques in SVG that can accomplish the same procedure using the SVG element’s viewPort setting, but even with these, at some point in time you’ll need to dust off your basic geometry skills if you want to work with graphics. However, as the example demonstrates, most of the math you’ll need is basic.

Page with SVG circle fit into rectangular div element
Figure 1-3. Page with SVG circle fit into rectangular div element

Calculating the Length of a Circular Arc

Problem

Given the radius of a circle, and the angle of an arc in degrees, find the length of the arc.

Solution

Use Math.PI to convert degrees to radians, and use the result in a formula to find the length of the arc:

// angle of arc is 120 degrees, radius of circle is 2
var radians = degrees * (Math.PI / 180);
var arclength = radians * radius; // value is 4.18879020478...

Discussion

The length of a circular arc is found by multiplying the circle’s radius times the angle of the arc, in radians.

If the angle is given in degrees, you’ll need to convert the degree to radians first, before multiplying the angle by the radius.

See Also

Converting Between Degrees and Radians covers how to convert between degrees and radians.

Using ES6 String Extras Without Leaving Users in the Dirt

Problem

You want to use new ECMAScript 6 features, such as the string extras like startsWith() and endsWith(), but you don’t want your applications to break for people using browsers that don’t support this newer functionality.

Solution

Use an ECMAScript 6 (or ES 6) shim to provide support for the functionality in browsers not currently implementing it. Example 1-7 demonstrates how a shim enables support for the new ES 6 String functionality.

Example 1-7. Using a shim to enable ES 6 functionality
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ES 6 String</title>
<script type="text/javascript" src="es6-shim.js"></script>
</head>
<body>

<script type="text/javascript">

  // quote from "To Kill a Mockingbird"
  var str = "Mockingbirds don't do one thing except make music " +
  "for us to enjoy. They don't eat up people's gardens, " +
  "don't nest in corn cribs, " +
  "they don’t do one thing but sing their hearts out for us. " +
  "That's why it’s a sin to kill a mockingbird."

  console.log(str.startsWith("Mockingbirds")); // true
  console.log(str.startsWith("autos", 20)); // false

  console.log(str.endsWith("mockingbird.")); // true
  console.log(str.endsWith("kill", str.length-15)); // true

  var cp = str.codePointAt(50); // 102 for 'f'
  var cp2 = str.codePointAt(51); // 111 for 'o'
  var cp3 = str.codePointAt(52); // 114 for 'r'

  var str2 = String.fromCodePoint(cp,cp2,cp3);

  console.log(str2); // for
</script>
</body>
</html>

Discussion

JavaScript (or ECMAScript, the more proper name) is advancing much more rapidly now than in the past, but uneven implementation is still an issue. We do live in better times, as the major browser companies are more ready to embrace new features more quickly, and automated browser upgrades help eliminate some of the bogging down we had with a browser such as IE 6. In addition, until we see complete cross-browser support for a new feature, we can still make use of enhancements in Node.js applications on the server, and via the use of shims in the client. I’ll cover Node.js in a later chapter, but for now, let’s look at shims, JavaScript compatibility, and what they mean for something like the new String object enhancements.

Note

The shim used in the example is the ES6-shim created by Paul Miller. There are other shims and libraries known as polyfills, which you’ll see used elsewhere in this book.

The latest formal release of ECMAScript (ES) is ECMAScript 5, and I make use of several ES 5 features throughout the book. Work is underway, though, on the next generation of ES, appropriately named ES.Next (ECMA-262 Edition 6), but commonly referred to as ES 6.

As consensus is reached on new ES features, they’re added to the existing draft specification. They’re also listed in ES compatibility tables, such as the ones Mozilla incorporates in much of its documentation, and the exceedingly helpful ECMAScript 6 Compatibility Table.

Among the ES 6 additions are the following new String.prototype methods:

  • startsWith: Returns true if string begins with characters from another string
  • endsWith: Returns true if string ends with characters from another string
  • contains: Returns true if string contains another string
  • repeat: Repeats the string a given number of times and returns the result
  • codePointAt: Returns the Unicode code point (unicode number) that starts at the given index

Both startsWith() and endsWith() require a string to examine as first parameter, and an optional integer as second parameter. For startsWith(), the integer marks the position in the string to begin the search; for endsWith(), the integer represents the position in the string where the search should terminate.

The contains() method also takes two parameters—search string and optional starting position for search—but it returns true or false depending on whether it found the search string anywhere in the string:

console.log(str.contains("gardens")); // true

The repeat() method takes a given string and repeats it however many times is given in the only parameter, returning the result:

var str2 = 'abc';
console.log(str2.repeat(2)); // abcabc

The codePointAt() method returns the UTF-16 encoded code point value for the character found at the position in the string. In addition, there’s also a new static method, fromCodePoint, which returns a string created by a sequence of code points:

var cp = str.codePointAt(50); // 102 for 'f'
var cp2 = str.codePointAt(51); // 111 for 'o'
var cp3 = str.codePointAt(52); // 114 for 'r'

var str2 = String.fromCodePoint(cp,cp2,cp3);

console.log(str2); // for

At the time of this writing, if I were to access the web page in Example 1-7 without the use of the ES 6 shim, the JavaScript would fail for all but a developer release of Firefox. With the use of the shim, the JavaScript works for all modern browsers.

See Also

Another alternative to a shim is a transpiler that compiles tomorrow’s code into today’s environment. Google’s version, Traceur, is introduced in Using a Destructuring Assignment to Simplify Code, and demonstrated more fully in Creating a True Class and Extending It (with a Little Help from Traceur).

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required