ponyfoo.com

Practical Modern JavaScript

Dive into ES6 and the future of JavaScript — Modular JavaScript Book Series
O’Reilly Media334 PagesISBN 978-1-4919-4353-3

“I am delighted to support Nicolás’ endeavor because his book looks exactly like what people who are coming to JavaScript with fresh eyes need.”

– Brendan Eich

Ideal for professional software developers with a basic understanding of JavaScript, this practical book shows you how to build small, interconnected ES6 JavaScript modules that emphasize reusability. You’ll learn how to face a project with a modular mindset, and how to organize your applications into simple pieces that work well in isolation and can be combined to create a large, robust application.

This book focuses on two aspects of JavaScript development: modularity and ES6 features. You’ll learn how to tackle application development by following a scale-out approach. As pieces of your codebase grow too big, you can break them up into smaller modules.

The book can be read online for free or purchased through Amazon.

This book is part of the Modular JavaScript series.

🗞 Start with the book series launch announcement on Pony Foo
💳 Participate in the crowdfunding campaign on Indiegogo
🌩 Amplify the announcement on social media via Thunderclap
🐤 Share a message on Twitter or within your social circles
👏 Contribute to the source code repository on GitHub
🦄 Read the free HTML version of the book on Pony Foo
📓 Purchase the book from O’Reilly on Amazon

Chapter 7

Built-in Improvements in ES6

Thus far in the book, we’ve discussed entirely new language syntax, such as property value shorthands, arrow functions, destructuring, or generators; and entirely new built-ins, such as WeakMap, Proxy, or Symbol. This chapter, on the other hand, is mostly devoted to existing built-ins that were improved in ES6. These improvements consist mostly of new instance methods, properties, and utility methods.

Numbers

ES6 introduces numeric literal representations for binary and octal numbers.

Binary and Octal Literals

Before ES6, your best bet when it comes to binary representation of integers was to just pass them to parseInt with a radix of 2.

parseInt('101', 2)
// <- 5

You can now use the new 0b prefix to represent binary integer literals. You could also use the 0B prefix, with a capital B. The two notations are equivalent.

console.log(0b000) // <- 0
console.log(0b001) // <- 1
console.log(0b010) // <- 2
console.log(0b011) // <- 3
console.log(0b100) // <- 4
console.log(0b101) // <- 5
console.log(0b110) // <- 6
console.log(0b111) // <- 7

In ES3, parseInt interpreted strings of digits starting with a 0 as an octal value. That meant things got weird quickly when you forgot to specify a radix of 10. As a result, specifying the radix of 10 became a best practice, so that user input like 012 wouldn’t unexpectedly be parsed as the integer 10.

console.log(parseInt('01'))
// <- 1
console.log(parseInt('012'))
// <- 10
console.log(parseInt('012', 10))
// <- 12

When ES5 came around, the default radix in parseInt changed, from 8 to 10. It was still recommended that you specified a radix for backward compatibility purposes. If you wanted to parse strings as octal values, you could explicitly pass in a radix of 8 as the second argument.

console.log(parseInt('100', 8))
// <- 64

You can now use the 0o prefix for octal literals, which are new in ES6. You could also use 0O, which is equivalent. Having a 0 followed by an uppercase O may be hard to distinguish in some typefaces, which is why it is suggested that you stick with the lowercase 0o notation.

console.log(0o001) // <- 1
console.log(0o010) // <- 8
console.log(0o100) // <- 64

You might be used to hexadecimal literals present in other languages, commonly prefixed with 0x. Those were already introduced to the JavaScript language in ES5. The prefix for literal hexadecimal notation is either 0x, or 0X, as shown in the following code snippet.

console.log(0x0ff) // <- 255
console.log(0xf00) // <- 3840

Besides these minor syntax changes where octal and binary literals were introduced, a few methods were added to Number in ES6. The first four Number methods that we’ll be discussing—Number.isNaN, Number.isFinite, Number.parseInt, and Number.parseFloat—already existed as functions in the global namespace. In addition, the methods in Number are slightly different in that they don’t coerce nonnumeric values into numbers before producing a result.

Number.isNaN

This method is almost identical to the global isNaN method. Number.isNaN returns whether the provided value is NaN, whereas isNaN returns whether value is not a number. These two questions have slightly different answers.

The next snippet quickly shows that, when passed to Number.isNaN, anything that’s not NaN will return false, while NaN will produce true. Note how in the last case we’re already passing NaN to Number.isNaN, as that’s the result of dividing two strings.

Number.isNaN(123)
// <- false, integers are not NaN
Number.isNaN(Infinity)
// <- false, Infinity is not NaN
Number.isNaN('a hundred')
// <- false, 'a hundred' is not NaN
Number.isNaN(NaN)
// <- true, NaN is NaN
Number.isNaN('a hundred' / 'two')
// <- true, 'a hundred' / 'two' is NaN, NaN is NaN

The isNaN method, in contrast, casts nonnumeric values passed to it before evaluating them against NaN. This results in significantly different return values. In the following example, each alternative produces different results because isNaN, unlike Number.isNaN, casts the value passed to it through Number first.

isNaN('a hundred')
// <- true, because Number('a hundred') is NaN
isNaN(new Date())
// <- false, because Number(new Date()) uses Date#valueOf,
//    which returns a unix timestamp

Number.isNaN is more precise than its global counterpart, because it doesn’t involve casting. There are still a few reasons why Number.isNaN can be a source of confusion.

First off, isNaN casts input through Number(value) before comparison, while Number.isNaN doesn’t. Neither Number.isNaN nor isNaN answer the “is this not a number?” question, but instead they answer whether value—or Number(value)—is NaN.

In most cases, what you actually want to know is whether a value identifies as a number—typeof NaN === number—and is a number. The isNumber function in the following code snippet does just that. Note that it’d work with both isNaN and Number.isNaN due to type checking. Everything that reports a typeof value of 'number' is a number, except for NaN, so we filter out those out as false positive results.

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

You can use that method to figure out whether a value is a number or not. In the next snippet there are a few examples of how isNumber works.

isNumber(1)
// <- true
isNumber(Infinity)
// <- true
isNumber(NaN)
// <- false
isNumber('two')
// <- false
isNumber(new Date())
// <- false

There is a function, which was already in the language, that somewhat resembles our custom isNumber function: isFinite.

1
IEEE 754 is the Floating Point Standard.
2
Dr. Axel Rauschmayer points this out in the article “New number and Math features in ES6”.
3
You can go deeper into functional Array methods by reading the article “Fun with Native Arrays”.
4
String#repeat in ECMAScript 6 Specification, section 21.1.3.13.
6
I recommend you read “JavaScript has a Unicode problem” from Mathias Bynens. In the article, Mathias analyzes JavaScript’s relationship with Unicode.
7
Emoji popularize this with glyphs sometimes made up of four code points. See this list of emoji made up of several code points.
8
For more details around the u flag in regular expressions, read “Unicode-aware regular expressions in ECMAScript 6” from Mathias Bynens.
Unlock with one Tweet!
Grants you full online access to Practical Modern JavaScript!
You can also read the book on the public git repository, but it won’t be as pretty! 😅