forked from marijnh/Eloquent-JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
01_values.txt
580 lines (469 loc) · 22 KB
/
01_values.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
:chap_num: 1
:prev_link: 00_intro
:next_link: 02_program_structure
= Values, Types, and Operators =
[quote, Master Yuan-Ma, The Book of Programming]
____
Below the surface of the machine, the program moves. Without effort,
it expands and contracts. In great harmony, electrons scatter and
regroup. The forms on the monitor are but ripples on the water. The
essence stays invisibly below.
____
(((data)))(((bit)))(((memory)))Inside the computer's world,
there is only data. We can read data, modify data, create new data—but
anything that isn't data simply does not exist. All this
data is stored as long sequences of bits, and is thus fundamentally
alike.
Bits are any kinds of two-valued things, usually described as
zeroes and ones. Inside the computer, they take forms like a high or
low electrical charge, a strong or weak signal, or a shiny or dull
spot on the surface of a CD. Any piece of discrete information can be
reduced to a sequence of zeros and ones, and thus represented in
bits.
For example, think about how you might show the number thirteen
in bits. It works the same way we write decimal numbers, but
instead of ten different digits, we have only two, and the weight of
each increases by a factor of two from right to left. Here are the
bits that make up the number thirteen, with the weights of the digits
shown below them:
----
0 0 0 0 1 1 0 1
128 64 32 16 8 4 2 1
----
So that's 00001101, or 8 + 4 + 1, which equals 13.
== Values ==
Imagine a sea of bits. An ocean of them. A typical modern computer
has over thirty billion bits in its volatile data
storage. Non-volatile storage (the hard disk or
equivalent) tends to have yet a few orders of magnitude more.
image::img/bit-sea.png[alt="The Ocean of Bits"]
To be able to work with such quantities of bits without getting lost, you can separate them into chunks
that represent pieces of information. In a JavaScript environment,
those chunks are called _((value))s_. Though all values are made of
bits, they play different roles. Every value has a ((type)) that
determines its role. There are six basic types of values in
JavaScript: numbers, strings, booleans, objects, functions, and
undefined values.
(((garbage collection)))To create a value, one must merely invoke its
name. This is very convenient. You don't have to gather building
material for your values or pay for them. You just call for one, and
_woosh_, you have it. They are not created from thin air, of course.
Every value has to be stored somewhere, and if you want to use a
gigantic amount of them at the same time you might run out of bits.
Fortunately, this is a problem only if you need them all
simultaneously. As soon as you no longer use a value, it will
dissipate, leaving behind its bits to be recycled as building material
for the next generation of values.
This chapter introduces the atomic elements of JavaScript programs:
Simple value types, and the operators that can act on such values.
== Numbers ==
(((Number type)))Values of the _number_ type are, unsurprisingly,
numeric values. In a JavaScript program, they are written as numbers
usually are:
[source,javascript]
----
13
----
(((binary)))Use that in a program, and it will cause the bit pattern
for the number 13 to come into existence inside the computer's memory.
JavaScript uses a fixed number of bits, namely 64 of them, to store a
single number value. There are only so many patterns you can make with
64 bits, which means that the amount of different numbers that can be
represented is limited. For N decimal digits, the amount of numbers
that can be represented is 10^N^. Similarly, given 64 binary digits,
you can represent 2^64^ different numbers, which is about 18 quintillion
(an 18 with 18 zeroes after it). This is a lot.
Computer memory used to be a lot smaller, and people tended to use groups of
eight or sixteen bits to represent their numbers. It was easy to
accidentally _overflow_ such small numbers, to end up with a number that
did not fit in the given amount of bits. Today, even personal computers have plenty of
memory, so we are free to use 64-bit chunks, which means we only need to worry about
overflow when dealing with truly astronomical numbers.
Not all whole numbers below 18 quintillion fit in a JavaScript number
though. Those bits also store negative numbers, so one bit indicates the
sign of the number. A bigger issue is that nonwhole numbers must also be
represented. To do this, some of the bits are used to store the position
of the decimal point. So the actual maximum whole number that can be stored is more
in the range of nine quadrillion (15 zeroes), which is still pleasantly huge.
Fractional numbers are written by using a dot:
[source,javascript]
----
9.81
----
(((scientific notation)))For very big or very small numbers, one can
also use scientific notation by adding an ‘e’, followed by the
exponent of the number:
[source,javascript]
----
2.998e8
----
That is 2.998 × 10^8^ = 299800000.
Calculations with whole numbers (also called _((integer))s_) smaller
than the aforementioned 9 quadrillion are guaranteed to always be
precise. Unfortunately, calculations with fractional numbers are
generally not. Just as π (pi) cannot be precisely expressed by a finite
amount of decimal digits, many numbers lose some precision when only
64 bits are available to store them. This is a shame, but it causes
practical problems only in very specific situations. The important
thing is to be aware of it and treat fractional digital numbers as
approximations, not as precise values.
=== Arithmetic ===
(((operator)))(((arithmetic)))(((addition)))(((multiplication)))
The main thing to do with numbers is arithmetic. Arithmetic operations
such as addition or multiplication take two number values and produce
a new number from them. Here is what they look like in JavaScript:
[source,javascript]
----
100 + 4 * 11
----
(((\* operator)))(((+ operator)))The ‘+’ and ‘*’ symbols are called
_operators_. The first stands for addition, and the second stands for
multiplication. Putting an operator between two values will
(((application,of operators)))apply it to those values and produce a
new value.
(((parentheses)))Does the example mean “add 4 and 100, and multiply
the result by 11”, or is the multiplication done before the adding?
As you might have guessed, the multiplication happens first. But, as
in mathematics, this can be changed by wrapping the addition in
parentheses:
[source,javascript]
----
(100 + 4) * 11
----
(((division)))(((subtraction)))(((- operator)))(((/ operator)))For
subtraction, there is the ‘-’ operator, and division can be done with
the ‘/’ operator.
When operators appear together without parentheses, the order in which
they are applied is determined by the _((precedence))_ of the
operators. The example shows that multiplication comes before
addition. ‘/’ has the same precedence as ‘*’. Likewise for ‘+’ and
‘-’. When multiple operators with the same precedence appear next to
each other (as in `1 - 2 + 1`), they are applied left to right.
These rules of precedence are not something you should worry about.
When in doubt, just add parentheses.
(((remainder operation)))(((% operator)))There is one more arithmetic
operator, which you might not recognize. The ‘%’ symbol is used to
represent the _remainder_ operation. `X % Y` is the remainder of
dividing `X` by `Y`. For example, `314 % 100` produces `14`, and `144
% 12` gives `0`. Remainder's precedence is the same as that of
multiplication and division. You'll often see this operator
referred to as “modulo”, though technically, “remainder” is more
accurate.
=== Special numbers ===
There are three special values in JavaScript that are considered
numbers but don't behave like normal numbers.
The first two are `Infinity` and `-Infinity`, which
represent the positive and negative infinities. `Infinity - 1` is
still `Infinity`, and so on. Don't put too much trust in infinity-based
computation. It isn't mathematically solid, and it
will quickly lead to our next special number: `NaN`.
`NaN` stands for “not a number”, even though it is a
value of the number type. You'll get this result when you say try to
calculate `0 / 0` (zero divided by zero), `Infinity - Infinity`, or
any number of other numeric operations that don't yield a precise,
meaningful result.
== Strings ==
(((String type)))(((textual data)))(((character)))The next basic data
type is the _string_. Strings are used to represent text. They are
written by enclosing their content in quotes:
[source,javascript]
----
"Patch my boat with chewing gum"
'Monkeys wave goodbye'
----
(((quoting (of strings))))Both single and double quotes can be used to
mark strings as long as the quotes at the start and the end of the
string match.
(((escaping,in strings)))(((newline)))Almost anything can be put
between quotes, and JavaScript will make a string value out of it. But
a few characters are more difficult. You can imagine how putting quotes
between quotes might be hard. _Newlines_ (the characters you get when you
press `enter`) also can't be put between quotes. The string has to
stay on a single line.
(((backslash)))To be able to have such characters in a string, the
following notation is used: Whenever a backslash (‘\’) is found
inside quoted text, it indicates that the character after it has a
special meaning. A quote that is preceded by a backslash will not end
the string, but be part of it. When an ‘n’ character occurs after a
backslash, it is interpreted as a newline. Similarly, a ‘t’ after a
backslash means a tab character. Take the following string:
[source,javascript]
----
"This is the first line\nAnd this is the second"
----
The actual text contained is this:
----
This is the first line
And this is the second
----
There are, of course, situations where you want a backslash in a
string to be just a backslash, not a special code. If two backslashes
follow each other, they will collapse together, and only one will be
left in the resulting string value. This is how the string “`A newline
character is written like "\n"`” can be written:
[source,javascript]
----
"A newline character is written like \"\\n\"."
----
(((+ operator)))(((concatenation)))Strings cannot be divided,
multiplied, or subtracted, but the ‘+’ operator _can_ be used on them.
It does not add, but it __concatenates__—it glues two strings together.
The following line will produce the string `"concatenate"`:
[source,javascript]
----
"con" + "cat" + "e" + "nate"
----
There are more ways of manipulating strings, which we will discuss
when we get to methods in Chapter 4.
== Unary operators ==
(((operator)))(((typeof operator)))(((type)))Not all operators are
symbols. Some are written as words. One example is the `typeof`
operator, which produces a string value naming the type of the value
you give it.
[source,javascript]
----
console.log(typeof 4.5)
// → number
console.log(typeof "x")
// → string
----
We will use `console.log` in example code to indicate that we want to
see the result of evaluating something. When you run such code, the
value produced should be shown on the screen, though how it appears will
depend on the JavaScript environment you use to run it.
(((- operator)))(((binary operator)))(((unary operator)))The other
operators we saw all operated on two values, `typeof` takes only one.
Operators that use two values are called _binary_ operators, while
those that take one are called _unary_ operators. The minus operator
can be used both as a binary operator and a unary operator:
[source,javascript]
----
console.log(- (10 - 2))
// → -8
----
== Boolean values ==
(((Boolean type)))(((operator)))(((true)))(((false)))Often, you will
need a value that simply distinguishes between two possiblities, like “yes”
and “no”, or “on” and “off”. For this, JavaScript has a _boolean_ type,
which has just two values: true and false (which are written
simply as those words).
=== Comparisons ===
Here is one way to produce boolean values:
[source,javascript]
----
console.log(3 > 2)
// → true
console.log(3 < 2)
// → false
----
(((> operator)))(((< operator)))(((greater than)))(((less
than)))(((comparing)))The ‘>’ and ‘<’ signs are the traditional symbols
for “is greater than” and “is less than”, respectively. They are binary operators,
applying them results in a boolean value that indicates
whether they hold true in this case.
Strings can be compared in the same way:
[source,javascript]
----
console.log("Aardvark" < "Zoroaster")
// → true
----
The way strings are ordered is more or less alphabetic: Uppercase
letters are always “less” than lowercase ones, so `"Z" < "a"` is
true, and nonalphabetic characters (‘!’, ‘-’, and so on) are also
included in the ordering. The actual comparison is
based on the _((Unicode))_ standard. This standard assigns a
number to virtually every character one would ever need, including
characters from Greek, Arabic, Japanese, Tamil, and so on. Having such
numbers is practical for storing strings inside a computer—you can
represent them as a sequence of numbers. When comparing strings,
JavaScript goes over them from left to right, comparing the numeric
codes of the characters one by one.
(((>= operator)))(((<= operator)))(((== operator)))(((!=
operator)))Other similar operators are ‘>=’ (greater than or equal
to), ‘\<=’ (less than or equal to), ‘==’ (equal to), and
‘!=’ (not equal to).
[source,javascript]
----
console.log("Itchy" != "Scratchy")
// → true
----
There is only one value in JavaScript that is not equal to itself, and
that is `NaN` (not-a-number).
[source,javascript]
----
console.log(NaN == NaN)
// → false
----
`NaN` is supposed to denote the result of a nonsensical computation, and
as such, it isn't equal to the result of any _other_ nonsensical computations.
=== Logical operators ===
(((logical operators)))There are also some operations that can be
applied to boolean values themselves. JavaScript supports three
logical operators: _and_, _or_, and _not_. These can be used to
“reason” about booleans.
(((&& operator)))The ‘&&’ operator represents logical _and_. It is a
binary operator, and its result is true only if both the values
given to it are true.
[source,javascript]
----
console.log(true && false)
// → false
console.log(true && true)
// → true
----
(((|| operator)))The ‘||’ operator denotes logical _or_. It produces
true if either of the values given to it is true:
[source,javascript]
----
console.log(false || true)
// → true
console.log(false || false)
// → false
----
(((negation)))(((! operator)))_Not_ is written as an exclamation mark
(‘!’). It is a unary operator that flips the value given to it—`!true`
produces `false` and `!false` gives `true`.
(((precedence)))When mixing these boolean operators with arithmetic
and other operators, it is not always obvious when parentheses are
needed. In practice, one can usually get by with knowing that of the
operators we have seen so far, ‘||’ has the lowest precedence, then
comes ‘&&’, then the comparison operators (‘>’, ‘==’, and so on), and
then the rest. This order has been chosen such that, in typical
expressions like the one below, as few parentheses as possible are
necessary.
[source,javascript]
----
1 + 1 == 2 || 10 * 10 <= 100
----
(((ternary operator)))(((?: operator)))The last logical operator I
want to discuss is not unary, not binary, but _ternary_, operating on
three values. It is written with a question mark and a colon, like
this:
[source,javascript]
----
console.log(true ? 1 : 2);
// → 1
console.log(false ? 1 : 2);
// → 2
----
This one is called the conditional operator (or sometimes just
“ternary operator”, since it is the only such operator in the
langauge). The value on the left of the question mark “picks” which of
the other two values will come out. When it is true, the middle value
is chosen, when it is false, the right-hand value comes out.
== Undefined values ==
(((undefined)))(((null)))There are two special values, written `null`
and `undefined`, that are used to denote the absence of a meaningful
value. They are themselves values, but they carry no
information.
Many operations in the language which don't produce a meaningful value
(we'll see some in the next chapter) will yield `undefined`, simply
because they have to yield _some_ value.
The difference in meaning between `undefined` and `null` is an accident
of JavaScript's design, and it doesn't matter most of the time. In the cases
where you actually have to concern yourself with these values, I
recommend treating them as interchangeable (more on that in a moment).
== Automatic type conversion ==
(((type conversion)))In the introduction, I mentioned that JavaScript
goes out of its way to accept almost any program you give it, even
if programs that do odd things. This is nicely demonstrated by these
expressions:
[source,javascript]
----
console.log(8 * null)
// → 0
console.log("5" - 1)
// → 4
console.log("5" + 1)
// → 51
console.log("five" * 2)
// → NaN
console.log(false == 0)
// → true
----
When an operator is applied to the “wrong” type of value, it will
quietly convert that value to the type it wants, using a set of rules
that often aren't what you want or expect. This is called _type
coercion_. So the `null` in the first expression becomes `0` and the
`"5"` in the second expression becomes `5` (from string to number).
Yet in the third expression, `+` tries string concatenation before
numeric addition, and so the `1` is converted to `"1"` (from number to
string).
When something that doesn't map to a number in an obvious way (such as
`"five"` or `undefined`) is converted to a number, the value `NaN` is
produced. Further arithmetic operations on `NaN` keep on producing
`NaN`, so if you find yourself getting one of those in an unexpected
place, look for accidental type conversions.
(((== operator)))When comparing values of the same type using ‘==’,
the outcome is easy to predict: you should get true when both
values are the same. But when the types differ, JavaScript uses a
complicated and confusing set of rules to determine what to do. I will
not explain them precisely, but in most cases, it just tries to convert
one of the values to the other value's type. However, when `null`
or `undefined` occurs on either side of the operator, it produces
true only if both sides are `null` or `undefined`.
That last piece of behavior is often very useful. When you want to
test whether a value has a real value instead of `null`
or `undefined`, you can simply compare it to `null` with the ‘==’ (or
‘!=’) operator.
(((=== operator)))(((!== operator)))But what if you want to test
whether something refers to the precise value `false`? The rules for
converting strings and numbers to boolean values state that `0`,
`NaN`, and the empty string (`""`) count as `false`, while all the other
values count as true. Because of this, expressions like `0 == false`
and `"" == false` are also true. For cases like this, where you do
_not_ want any automatic type conversions to happen, there are two
extra operators: ‘===’ and ‘!==’. The first tests whether a value is
precisely equal to the other, and the second tests whether it is not
precisely equal. So `"" === false` is false as expected.
I recommend using the three-character comparison operators defensively, to
prevent unexpected type conversions from tripping you up. But when you're
certain the types on both sides will be the same, there is no problem with
using the shorter operators.
=== Short-circuiting of logical operators ===
(((|| operator)))(((&& operator)))(((?: operator)))(((operator)))The
logical operators ‘&&’ and ‘||’ handle values of different types in a
peculiar way. They _will_ convert the value on their left side to
boolean type in order to decide what to do, but depending on the
operator and the result of that conversion, they either return the
_original_ left-hand value, or the right-hand value.
The ‘||’ operator, for example, will return the value to its left when that can be
converted to true, and the value on its right otherwise. This conversion works as
you'd expect for boolean values and should do something analogous for
values of other types.
[source,javascript]
----
console.log(null || "user")
// → user
console.log("Karl" || "user")
// → Karl
----
This functionality allows the `||` operator to be used as a way
to fall back on a default value. If you give it an expression that might produce
an empty value on the left, the value on the right will be used as a replacement
in that case.
The ‘&&’ operator works similarly, but the other way around. When the
value to its left is something that converts to false, it returns that
value, and otherwise it returns the value on its right.
(((short-circuit evaluation)))Another important property of these two
operators is that the expression to their right is evaluated only when
necessary. In the case of `true || X`, no matter what `X` is—even if
it's an expression that does something __terrible__—the result will be
true, and `X` is never evaluated. The same goes for `false && X`,
which is false, and will ignore `X`. This is called _short-circuit evaluation_.
The conditional operator work in a similar way. The first expression
is always evaluated, but the second or third value, the one that is
not picked, is also not evaluated.
== Summary ==
We looked at four types of JavaScript values in this chapter: numbers,
strings, booleans, and undefined values.
Such values are created by typing in their name (`true`, `null`) or
value (`13`, `"abc"`). You can combine and transform values with operators. We saw binary
operators for arithmetic (‘+’, ‘-’, ‘*’, ‘/’,
and ‘%’), string concatenation (‘+’), comparison (‘==’, ‘!=’,
‘===’, ‘!==’, ‘<’, ‘>’, ‘\<=’, ‘>=’), and logic (‘&&’, ‘||’), as well
as several unary operators (‘-’ to negate a number, ‘!’ to negate
logically, and `typeof` to find a value's type).
This gives you enough information to use JavaScript as a pocket
calculator, but not much more. The next chapter will start tying these
basic expressions together into basic programs.