“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
.
Array
methods by reading the article “Fun with Native Arrays”.String#repeat
in ECMAScript 6 Specification, section 21.1.3.13.u
flag in regular expressions, read “Unicode-aware regular expressions in ECMAScript 6” from Mathias Bynens.dotAll
flag proposal document.String#matchAll
proposal document.