1
0
mirror of https://github.com/apachecn/eloquent-js-3e-zh.git synced 2025-05-23 20:02:20 +00:00
This commit is contained in:
wizardforcel 2018-04-28 14:32:58 +08:00
commit 48c8d93b4a
5 changed files with 3593 additions and 0 deletions

273
diff-en/2ech1-3ech1.diff Normal file
View File

@ -0,0 +1,273 @@
diff --git a/2ech1.md b/3ech1.md
index 33d8537..57e5b20 100644
--- a/2ech1.md
+++ b/3ech1.md
@@ -4,28 +4,26 @@
>
> <footer>Master Yuan-Ma, <cite>The Book of Programming</cite></footer>
-Inside the computer's world, there is only data. You 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.
+Inside the computer's world, there is only data. You can read data, modify data, create new data—but that which isn't data cannot be mentioned. All this data is stored as long sequences of bits and is thus fundamentally alike.
-Bits are any kind of two-valued things, usually described as zeros and ones. Inside the computer, they take forms such as 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.
+_Bits_ are any kind of two-valued things, usually described as zeros and ones. Inside the computer, they take forms such as 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 13 in bits. It works the same way you write decimal numbers, but instead of 10 different digits, you have only 2, and the weight of each increases by a factor of 2 from right to left. Here are the bits that make up the number 13, with the weights of the digits shown below them:
+For example, we can express the number 13 in bits. It works the same way as a decimal number, but instead of 10 different digits, you have only 2, and the weight of each increases by a factor of 2 from right to left. Here are the bits that make up the number 13, 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 the binary number 00001101, or 8 + 4 + 1, which equals 13.
+So that's the binary number 00001101, or 8 + 4 + 1, or 13.
## Values
-Imagine a sea of bits. An ocean of them. A typical modern computer has more than 30 billion bits in its volatile data storage. Nonvolatile storage (the hard disk or equivalent) tends to have yet a few orders of magnitude more.
+Imagine a sea of bits—an ocean of them. A typical modern computer has more than 30 billion bits in its volatile data storage (working memory). Nonvolatile storage (the hard disk or equivalent) tends to have yet a few orders of magnitude more.
-![The Ocean of Bits](img/bit-sea.png)
+To be able to work with such quantities of bits without getting lost, we must separate them into chunks that represent pieces of information. In a JavaScript environment, those chunks are called _values_. Though all values are made of bits, they play different roles. Every value has a type that determines its role. Some values are numbers, some values are pieces of text, some values are functions, and so on.
-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 _values_. 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.
-
-To create a value, you must merely invoke its name. This is 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.
+To create a value, you must merely invoke its name. This is 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 really 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 memory. 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, that is, the simple value types and the operators that can act on such values.
@@ -39,19 +37,19 @@ Values of the _number_ type are, unsurprisingly, numeric values. In a JavaScript
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<sup>_N_</sup>. Similarly, given 64 binary digits, you can represent 2<sup>64</sup> different numbers, which is about 18 quintillion (an 18 with 18 zeros after it). This is a lot.
+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<sup>N</sup>. Similarly, given 64 binary digits, you can represent 2<sup>64</sup> different numbers, which is about 18 quintillion (an 18 with 18 zeros after it). That's a lot.
-Computer memory used to be a lot smaller, and people tended to use groups of 8 or 16 bits to represent their numbers. It was easy to accidentally _overflow_ such small numbers—to end up with a number that did not fit into the given amount of bits. Today, even personal computers have plenty of memory, so you are free to use 64-bit chunks, which means you need to worry about overflow only when dealing with truly astronomical numbers.
+Computer memory used to be much smaller, and people tended to use groups of 8 or 16 bits to represent their numbers. It was easy to accidentally _overflow_ such small numbers—to end up with a number that did not fit into the given amount of bits. Today, even computers that fit in your pocket have plenty of memory, so you are free to use 64-bit chunks, and you need to worry about overflow only 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. The actual maximum whole number that can be stored is more in the range of 9 quadrillion (15 zeros), which is still pleasantly huge.
+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. The actual maximum whole number that can be stored is more in the range of 9 quadrillion (15 zeros)—which is still pleasantly huge.
-Fractional numbers are written by using a dot.
+Fractional numbers are written by using a dot:
```
9.81
```
-For very big or very small numbers, you can also use scientific notation by adding an “e” (for “exponent”), followed by the exponent of the number:
+For very big or very small numbers, you may also use scientific notation by adding an _e_ (for _exponent_), followed by the exponent of the number:
```
2.998e8
@@ -71,7 +69,7 @@ The main thing to do with numbers is arithmetic. Arithmetic operations such as a
The `+` and `*` symbols are called _operators_. The first stands for addition, and the second stands for multiplication. Putting an operator between two values will apply it to those values and produce a new value.
-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, you can change this by wrapping the addition in parentheses.
+But 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, you can change this by wrapping the addition in parentheses:
```
(100 + 4) * 11
@@ -83,28 +81,29 @@ When operators appear together without parentheses, the order in which they are
These rules of precedence are not something you should worry about. When in doubt, just add parentheses.
-There is one more arithmetic operator, which you might not immediately 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.
+There is one more arithmetic operator, which you might not immediately 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 also often see this operator referred to as _modulo_.
### 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`.
+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, though. It isn't mathematically sound, 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, for example, 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.
+`NaN` stands for “not a number”, even though it _is_ a value of the number type. You'll get this result when you, for example, try to calculate `0 / 0` (zero divided by zero), `Infinity - Infinity`, or any number of other numeric operations that don't yield a meaningful result.
## Strings
-The next basic data type is the _string_. Strings are used to represent text. They are written by enclosing their content in quotes.
+The next basic data type is the _string_. Strings are used to represent text. They are written by enclosing their content in quotes:
```
-"Patch my boat with chewing gum"
-'Monkeys wave goodbye'
+`Down on the sea`
+"Lie on the ocean"
+'Float on the ocean'
```
-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.
+You can use single quotes, double quotes, or backticks to mark strings, as long as the quotes at the start and the end of the string match.
-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.
+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) may only be included without escaping when the string is quoted with backticks (```).
To make it possible to include 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. This is called _escaping_ the character. 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:
@@ -119,19 +118,31 @@ 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 expressed:
+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 expressed:
```
"A newline character is written like \"\\n\"."
```
+Strings, too, have to be modeled as a series of bits to be able to exist inside the computer. The way JavaScript does this is based on the _Unicode_ standard. This standard assigns a number to virtually every character you would ever need, including characters from Greek, Arabic, Japanese, Armenian, and so on. If we have a number for every character, a string can be described by a sequence of numbers.
+
+And that's what JavaScript does. But there's a complication: JavaScript's representation uses 16 bits per string element, which can describe up to 2<sup>16</sup> different characters. But Unicode defines more characters than that—about twice as many, at this point. So some characters, such as many emoji, take up two “character positions” in JavaScript strings. We'll come back to this in [Chapter 5](05_higher_order.html#code_units).
+
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"`:
```
"con" + "cat" + "e" + "nate"
```
-There are more ways of manipulating strings, which we will discuss when we get to methods in [Chapter 4](04_data.html#methods).
+String values have a number of associated functions (_methods_) that can be used to perform other operations on them. We'll come back to these in [Chapter 4](04_data.html#methods).
+
+Strings written with single or double quotes behave very much the same—the only difference is in which type of quote you need to escape inside of them. Backtick-quoted strings, usually called _template literals_, can do a few more tricks. Apart from being able to span lines, they can also embed other values.
+
+```
+`half of 100 is ${100 / 2}`
+```
+
+When you write something inside `${}` in a template literal, its result will be computed, converted to a string, and included at that position. The example produces “_half of 100 is 50_”.
## Unary operators
@@ -144,7 +155,7 @@ 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.
+We will use `console.log` in example code to indicate that we want to see the result of evaluating something. More about that in the [next chapter](02_program_structure.html).
The other operators we saw all operated on two values, but `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 as a unary operator.
@@ -155,9 +166,9 @@ console.log(- (10 - 2))
## Boolean values
-Often, you will need a value that simply distinguishes between two possibilities, 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).
+It is often useful to have a value that distinguishes between only two possibilities, like “yes” and “no” or “on” and “off”. For this purpose, JavaScript has a _Boolean_ type, which has just two values: true and false, which are written as those words.
-### Comparisons
+### Comparison
Here is one way to produce Boolean values:
@@ -177,16 +188,18 @@ 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" &lt; "a"` is true, and non-alphabetic 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 you would ever need, including characters from Greek, Arabic, Japanese, Tamil, and so on. Having such numbers is useful for storing strings inside a computer because it makes it possible to 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.
+The way strings are ordered is roughly alphabetic, but not really what you'd expect to see in a dictionary: uppercase letters are always “less” than lowercase ones, so `"Z" &lt; "a"`, and nonalphabetic characters (!, -, and so on) are also included in the ordering. When comparing strings, JavaScript goes over the characters from left to right, comparing the Unicode codes one by one.
Other similar operators are `&gt;=` (greater than or equal to), `&lt;=` (less than or equal to), `==` (equal to), and `!=` (not equal to).
```
console.log("Itchy" != "Scratchy")
// → true
+console.log("Apple" == "Orange")
+// → false
```
-There is only one value in JavaScript that is not equal to itself, and that is `NaN`, which stands for “not a number”.
+There is only one value in JavaScript that is not equal to itself, and that is `NaN` (“not a number”).
```
console.log(NaN == NaN)
@@ -234,19 +247,19 @@ 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 language). 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, and when it is false, the value on the right comes out.
+This one is called the _conditional_ operator (or sometimes just _ternary_ operator since it is the only such operator in the language). The value on the left of the question mark “picks” which of the other two values will come out. When it is true, it chooses the middle value, and when it is false, the value on the right.
-## Undefined values
+## Empty values
-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.
+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 that don't produce a meaningful value (you'll see some later) 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).
+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 mostly interchangeable.
## Automatic type conversion
-In the introduction, I mentioned that JavaScript goes out of its way to accept almost any program you give it, even programs that do odd things. This is nicely demonstrated by the following expressions:
+In the Introduction, I mentioned that JavaScript goes out of its way to accept almost any program you give it, even programs that do odd things. This is nicely demonstrated by the following expressions:
```
console.log(8 * null)
@@ -261,9 +274,9 @@ console.log(false == 0)
// → true
```
-When an operator is applied to the “wrong” type of value, JavaScript 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, so the `1` is converted to `"1"` (from number to string).
+When an operator is applied to the “wrong” type of value, JavaScript will quietly convert that value to the type it needs, using a set of rules that often aren't what you want or expect. This is called _type coercion_. 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, 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 producing `NaN`, so if you find yourself getting one of those in an unexpected place, look for accidental type conversions.
+When something that doesn't map to a number in an obvious way (such as `"five"` or `undefined`) is converted to a number, you get the value `NaN`. Further arithmetic operations on `NaN` keep producing `NaN`, so if you find yourself getting one of those in an unexpected place, look for accidental type conversions.
When comparing values of the same type using `==`, the outcome is easy to predict: you should get true when both values are the same, except in the case of `NaN`. But when the types differ, JavaScript uses a complicated and confusing set of rules to determine what to do. 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 one of `null` or `undefined`.
@@ -274,32 +287,32 @@ console.log(null == 0);
// → false
```
-That last piece of behavior is often 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.
+That behavior is often useful. When you want to test whether a value has a real value instead of `null` or `undefined`, you can compare it to `null` with the `==` (or `!=`) 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.
+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. When you do _not_ want any automatic type conversions to happen, there are two additional 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
-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 return either the _original_ left-hand value or the right-hand value.
+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 will return either 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 will return 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.
+The `||` operator, for example, will return the value to its left when that can be converted to true and will return the value on its right otherwise. This has the expected effect when the values are Boolean, and does something analogous for values of other types.
```
console.log(null || "user")
// → user
-console.log("Karl" || "user")
-// → Karl
+console.log("Agnes" || "user")
+// → Agnes
```
-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.
+We can use this functionality as a way to fall back on a default value. If you have a value that might be empty, you can put `||` after it with a replacement value. If the initial value can be converted to false, you'll get the replacement instead.
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.
-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_.
+Another important property of these two operators is that the part to their right is evaluated only when necessary. In the case of `true || X`, no matter what `X` is—even if it's a piece of program 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 works in a similar way. The first expression is always evaluated, but the second or third value, the one that is not picked, is not.
+The conditional operator works in a similar way. Of the second and third value, only the one that is selected is evaluated.
## Summary
@@ -307,4 +320,4 @@ We looked at four types of JavaScript values in this chapter: numbers, strings,
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 (`==`, `!=`, `===`, `!==`, `&lt;`, `&gt;`, `&lt;=`, `&gt;=`), and logic (`&&`, `||`), as well as several unary operators (`-` to negate a number, `!` to negate logically, and `typeof` to find a value's type) and a ternary operator (`?:`) to pick one of two values based on a third value.
-This gives you enough information to use JavaScript as a pocket calculator, but not much more. The [next chapter](02_program_structure.html#program_structure) will start tying these expressions together into basic programs.
+This gives you enough information to use JavaScript as a pocket calculator, but not much more. The [next chapter](02_program_structure.html) will start tying these expressions together into basic programs.

697
diff-en/2ech2-3ech2.diff Normal file
View File

@ -0,0 +1,697 @@
diff --git a/2ech2.md b/3ech2.md
index 645a197..80cf5d8 100644
--- a/2ech2.md
+++ b/3ech2.md
@@ -4,17 +4,17 @@
>
> &lt;footer&gt;_why, &lt;cite&gt;Why's (Poignant) Guide to Ruby&lt;/cite&gt;&lt;/footer&gt;
-In this chapter, we will start to do things that can actually be called _programming_. We will expand our command of the JavaScript language beyond the nouns and sentence fragments we've seen so far, to the point where we can express some meaningful prose.
+In this chapter, we start to do things that can actually be called _programming_. We will expand our command of the JavaScript language beyond the nouns and sentence fragments we've seen so far, to the point where we can express meaningful prose.
## Expressions and statements
-In [Chapter 1](01_values.html#values), we made some values and then applied operators to them to get new values. Creating values like this is an essential part of every JavaScript program, but it is only a part.
+In [Chapter 1](01_values.html), we made values and applied operators to them to get new values. Creating values like this is the main substance of any JavaScript program. But that substance has to be framed in a larger structure to be useful. So that's what we'll get to next.
A fragment of code that produces a value is called an _expression_. Every value that is written literally (such as `22` or `"psychoanalysis"`) is an expression. An expression between parentheses is also an expression, as is a binary operator applied to two expressions or a unary operator applied to one.
-This shows part of the beauty of a language-based interface. Expressions can nest in a way very similar to the way subsentences in human languages are nested—a subsentence can contain its own subsentences, and so on. This allows us to combine expressions to express arbitrarily complex computations.
+This shows part of the beauty of a language-based interface. Expressions can contain other expressions in a way very similar to the way subsentences in human languages are nested—a subsentence can contain its own subsentences, and so on. This allows us to build expressions that describe arbitrarily complex computations.
-If an expression corresponds to a sentence fragment, a JavaScript _statement_ corresponds to a full sentence in a human language. A program is simply a list of statements.
+If an expression corresponds to a sentence fragment, a JavaScript _statement_ corresponds to a full sentence. A program is a list of statements.
The simplest kind of statement is an expression with a semicolon after it. This is a program:
@@ -23,36 +23,34 @@ The simplest kind of statement is an expression with a semicolon after it. This
!false;
```
-It is a useless program, though. An expression can be content to just produce a value, which can then be used by the enclosing expression. A statement stands on its own and amounts to something only if it affects the world. It could display something on the screen—that counts as changing the world—or it could change the internal state of the machine in a way that will affect the statements that come after it. These changes are called _side effects_. The statements in the previous example just produce the values `1` and `true` and then immediately throw them away. This leaves no impression on the world at all. When executing the program, nothing observable happens.
+It is a useless program, though. An expression can be content to just produce a value, which can then be used by the enclosing code. A statement stands on its own, so it amounts to something only if it affects the world. It could display something on the screen—that counts as changing the world—or it could change the internal state of the machine in a way that will affect the statements that come after it. These changes are called _side effects_. The statements in the previous example just produce the values `1` and `true` and then immediately throw them away. This leaves no impression on the world at all. When you run this program, nothing observable happens.
-In some cases, JavaScript allows you to omit the semicolon at the end of a statement. In other cases, it has to be there, or the next line will be treated as part of the same statement. The rules for when it can be safely omitted are somewhat complex and error-prone. In this book, every statement that needs a semicolon will always be terminated by one. I recommend you do the same in your own programs, at least until you've learned more about subtleties involved in leaving out semicolons.
+In some cases, JavaScript allows you to omit the semicolon at the end of a statement. In other cases, it has to be there, or the next line will be treated as part of the same statement. The rules for when it can be safely omitted are somewhat complex and error-prone. So in this book, every statement that needs a semicolon will always get one. I recommend you do the same, at least until you've learned more about the subtleties of missing semicolons.
-## Variables
+## Bindings
-How does a program keep an internal state? How does it remember things? We have seen how to produce new values from old values, but this does not change the old values, and the new value has to be immediately used or it will dissipate again. To catch and hold values, JavaScript provides a thing called a _variable_.
+How does a program keep an internal state? How does it remember things? We have seen how to produce new values from old values, but this does not change the old values, and the new value has to be immediately used or it will dissipate again. To catch and hold values, JavaScript provides a thing called a _binding_, or _variable_:
```
-var caught = 5 * 5;
+let caught = 5 * 5;
```
-And that gives us our second kind of statement. The special word (_keyword_) `var` indicates that this sentence is going to define a variable. It is followed by the name of the variable and, if we want to immediately give it a value, by an `=` operator and an expression.
+That's a second kind of statement. The special word (_keyword_) `let` indicates that this sentence is going to define a binding. It is followed by the name of the binding and, if we want to immediately give it a value, by an `=` operator and an expression.
-The previous statement creates a variable called `caught` and uses it to grab hold of the number that is produced by multiplying 5 by 5.
+The previous statement creates a binding called `caught` and uses it to grab hold of the number that is produced by multiplying 5 by 5.
-After a variable has been defined, its name can be used as an expression. The value of such an expression is the value the variable currently holds. Here's an example:
+After a binding has been defined, its name can be used as an expression. The value of such an expression is the value the binding currently holds. Here's an example:
```
-var ten = 10;
+let ten = 10;
console.log(ten * ten);
// → 100
```
-Variable names can be any word that isn't a reserved word (such as `var`). They may not include spaces. Digits can also be part of variable names—`catch22` is a valid name, for example—but the name must not start with a digit. A variable name cannot include punctuation, except for the characters `<article and `_`.
-
-When a variable points at a value, that does not mean it is tied to that value forever. The `=` operator can be used at any time on existing variables to disconnect them from their current value and have them point to a new one.
+When a binding points at a value, that does not mean it is tied to that value forever. The `=` operator can be used at any time on existing bindings to disconnect them from their current value and have them point to a new one:
```
-var mood = "light";
+let mood = "light";
console.log(mood);
// → light
mood = "dark";
@@ -60,182 +58,186 @@ console.log(mood);
// → dark
```
-You should imagine variables as tentacles, rather than boxes. They do not _contain_ values; they _grasp_ them—two variables can refer to the same value. A program can access only the values that it still has a hold on. When you need to remember something, you grow a tentacle to hold on to it or you reattach one of your existing tentacles to it.
-
-![Variables as tentacles](img/octopus.jpg)
+You should imagine bindings as tentacles, rather than boxes. They do not _contain_ values; they _grasp_ them—two bindings can refer to the same value. A program can only access the values that it still has a reference to. When you need to remember something, you grow a tentacle to hold on to it or you reattach one of your existing tentacles to it.
-Let's look at an example. To remember the number of dollars that Luigi still owes you, you create a variable. And then when he pays back $35, you give this variable a new value.
+Let's look at another example. To remember the number of dollars that Luigi still owes you, you create a binding. And then when he pays back $35, you give this binding a new value:
```
-var luigisDebt = 140;
+let luigisDebt = 140;
luigisDebt = luigisDebt - 35;
console.log(luigisDebt);
// → 105
```
-When you define a variable without giving it a value, the tentacle has nothing to grasp, so it ends in thin air. If you ask for the value of an empty variable, you'll get the value `undefined`.
+When you define a binding without giving it a value, the tentacle has nothing to grasp, so it ends in thin air. If you ask for the value of an empty binding, you'll get the value `undefined`.
-A single `var` statement may define multiple variables. The definitions must be separated by commas.
+A single `let` statement may define multiple bindings. The definitions must be separated by commas.
```
-var one = 1, two = 2;
+let one = 1, two = 2;
console.log(one + two);
// → 3
```
-## Keywords and reserved words
+The words `var` and `const` can also be used to create bindings, in a way similar to `let`.
+
+```
+var name = "Ayda";
+const greeting = "Hello ";
+console.log(greeting + name);
+// → Hello Ayda
+```
+
+The first, `var` (short for “variable”), is the way bindings were declared in pre-2015 JavaScript. We'll get back to the precise way it differs from `let` in the [next chapter](03_functions.html). For now, remember that it mostly does the same thing, but we'll rarely use it in this book because it has some confusing properties.
-Words with a special meaning, such as `var`, are _keywords_, and they may not be used as variable names. There are also a number of words that are “reserved for use” in future versions of JavaScript. These are also officially not allowed to be used as variable names, though some JavaScript environments do allow them. The full list of keywords and reserved words is rather long.
+The word `const` stands for _constant_. It defines a constant binding, which points at the same value for as long as it lives. This is useful for bindings that give a name to a value so that you can easily refer to it later.
+
+## Binding names
+
+Binding names can be any word. Digits can be part of binding names—`catch22` is a valid name, for example—but the name must not start with a digit. A binding name may include dollar signs (`<article) or underscores (`_`), but no other punctuation or special characters.
+
+Words with a special meaning, such as `let`, are _keywords_, and they may not be used as binding names. There are also a number of words that are “reserved for use” in future versions of JavaScript, which also can't be used as binding names. The full list of keywords and reserved words is rather long:
```
-break case catch class const continue debugger
-default delete do else enum export extends false
-finally for function if implements import in
-instanceof interface let new null package private
-protected public return static super switch this
-throw true try typeof var void while with yield
+break case catch class const continue debugger default
+delete do else enum export extends false finally for
+function if implements import interface in instanceof let
+new package private protected public return static super
+switch this throw true try typeof var void while with yield
```
-Don't worry about memorizing these, but remember that this might be the problem when a variable definition does not work as expected.
+Don't worry about memorizing these. When creating a binding produces an unexpected syntax error, see if you're trying to define a reserved word.
## The environment
-The collection of variables and their values that exist at a given time is called the _environment_. When a program starts up, this environment is not empty. It always contains variables that are part of the language standard, and most of the time, it has variables that provide ways to interact with the surrounding system. For example, in a browser, there are variables and functions to inspect and influence the currently loaded website and to read mouse and keyboard input.
+The collection of bindings and their values that exist at a given time is called the _environment_. When a program starts up, this environment is not empty. It always contains bindings that are part of the language standard, and most of the time, it also has bindings that provide ways to interact with the surrounding system. For example, in a browser, there are functions to interact with the currently loaded website and to read mouse and keyboard input.
## Functions
-A lot of the values provided in the default environment have the type _function_. A function is a piece of program wrapped in a value. Such values can be _applied_ in order to run the wrapped program. For example, in a browser environment, the variable `alert` holds a function that shows a little dialog box with a message. It is used like this:
+A lot of the values provided in the default environment have the type _function_. A function is a piece of program wrapped in a value. Such values can be _applied_ in order to run the wrapped program. For example, in a browser environment, the binding `prompt` holds a function that shows a little dialog box asking for user input. It is used like this:
```
-alert("Good morning!");
+prompt("Enter passcode");
```
-![An alert dialog](img/alert.png)
+<figure>![A prompt dialog](img/prompt.png)</figure>
-Executing a function is called _invoking_, _calling_, or _applying_ it. You can call a function by putting parentheses after an expression that produces a function value. Usually you'll directly use the name of the variable that holds the function. The values between the parentheses are given to the program inside the function. In the example, the `alert` function uses the string that we give it as the text to show in the dialog box. Values given to functions are called _arguments_. The `alert` function needs only one of them, but other functions might need a different number or different types of arguments.
+Executing a function is called _invoking_, _calling_, or _applying_ it. You can call a function by putting parentheses after an expression that produces a function value. Usually you'll directly use the name of the binding that holds the function. The values between the parentheses are given to the program inside the function. In the example, the `prompt` function uses the string that we give it as the text to show in the dialog box. Values given to functions are called _arguments_. Different functions might need a different number or different types of arguments.
+
+The `prompt` function isn't used much in modern web programming, mostly because you have no control over the way the resulting dialog looks, but can be helpful in toy programs and experiments.
## The console.log function
-The `alert` function can be useful as an output device when experimenting, but clicking away all those little windows will get on your nerves. In past examples, we've used `console.log` to output values. Most JavaScript systems (including all modern web browsers and Node.js) provide a `console.log` function that writes out its arguments to _some_ text output device. In browsers, the output lands in the JavaScript console. This part of the browser interface is hidden by default, but most browsers open it when you press F12 or, on Mac, when you press Command-Option-I. If that does not work, search through the menus for an item named “web console” or “developer tools”.
+In the examples, I used `console.log` to output values. Most JavaScript systems (including all modern web browsers and Node.js) provide a `console.log` function that writes out its arguments to _some_ text output device. In browsers, the output lands in the JavaScript console. This part of the browser interface is hidden by default, but most browsers open it when you press F12 or, on Mac, Command-Option-I. If that does not work, search through the menus for an item named “developer tools” or similar.
-When running the examples, or your own code, on the pages of this book, `console.log` output will be shown after the example, instead of in the browser's JavaScript console.
+When running the examples (or your own code) on the pages of this book, `console.log` output will be shown after the example, instead of in the browser's JavaScript console.
```
-var x = 30;
+let x = 30;
console.log("the value of x is", x);
// → the value of x is 30
```
-Though variable names cannot contain period characters, `console.log` clearly has one. This is because `console.log` isn't a simple variable. It is actually an expression that retrieves the `log` property from the value held by the `console` variable. We will find out exactly what this means in [Chapter 4](04_data.html#properties).
+Though binding names cannot contain period characters, `console.log` does have one. This is because `console.log` isn't a simple binding. It is actually an expression that retrieves the `log` property from the value held by the `console` binding. We will find out exactly what this means in [Chapter 4](04_data.html#properties).
## Return values
-Showing a dialog box or writing text to the screen is a _side effect_. A lot of functions are useful because of the side effects they produce. Functions may also produce values, and in that case, they don't need to have a side effect to be useful. For example, the function `Math.max` takes any number of number values and gives back the greatest.
+Showing a dialog box or writing text to the screen is a _side effect_. A lot of functions are useful because of the side effects they produce. Functions may also produce values, in which case they don't need to have a side effect to be useful. For example, the function `Math.max` takes any amount of number arguments and gives back the greatest.
```
console.log(Math.max(2, 4));
// → 4
```
-When a function produces a value, it is said to _return_ that value. Anything that produces a value is an expression in JavaScript, which means function calls can be used within larger expressions. Here a call to `Math.min`, which is the opposite of `Math.max`, is used as an input to the plus operator:
+When a function produces a value, it is said to _return_ that value. Anything that produces a value is an expression in JavaScript, which means function calls can be used within larger expressions. Here a call to `Math.min`, which is the opposite of `Math.max`, is used as part of a plus expression:
```
console.log(Math.min(2, 4) + 100);
// → 102
```
-The [next chapter](03_functions.html#functions) explains how to write your own functions.
+The [next chapter](03_functions.html) explains how to write your own functions.
-## prompt and confirm
+## Control flow
-Browser environments contain other functions besides `alert` for popping up windows. You can ask the user an OK/Cancel question using `confirm`. This returns a Boolean: `true` if the user clicks OK and `false` if the user clicks Cancel.
+When your program contains more than one statement, the statements are executed as if they are a story, from top to bottom. This example program has two statements. The first one asks the user for a number, and the second, which is executed after the first, shows the square of that number.
```
-confirm("Shall we, then?");
+let theNumber = Number(prompt("Pick a number"));
+console.log("Your number is the square root of " +
+ theNumber * theNumber);
```
-![A confirm dialog](img/confirm.png)
+The function `Number` converts a value to a number. We need that conversion because the result of `prompt` is a string value, and we want a number. There are similar functions called `String` and `Boolean` that convert values to those types.
-The `prompt` function can be used to ask an “open” question. The first argument is the question, the second one is the text that the user starts with. A line of text can be typed into the dialog window, and the function will return this text as a string.
+Here is the rather trivial schematic representation of straight-line control flow:
-```
-prompt("Tell me everything you know.", "...");
-```
+<figure>![Trivial control flow](img/controlflow-straight.svg)</figure>
-![An prompt dialog](img/prompt.png)
+## Conditional execution
-These two functions aren't used much in modern web programming, mostly because you have no control over the way the resulting windows look, but they are useful for toy programs and experiments.
+Not all programs are straight roads. We may, for example, want to create a branching road, where the program takes the proper branch based on the situation at hand. This is called _conditional execution_.
-## Control flow
+<figure>![Conditional control flow](img/controlflow-if.svg)</figure>
-When your program contains more than one statement, the statements are executed, predictably, from top to bottom. As a basic example, this program has two statements. The first one asks the user for a number, and the second, which is executed afterward, shows the square of that number.
+Conditional execution is created with the `if` keyword in JavaScript. In the simple case, we want some code to be executed if, and only if, a certain condition holds. We might, for example, want to show the square of the input only if the input is actually a number.
```
-var theNumber = Number(prompt("Pick a number", ""));
-alert("Your number is the square root of " +
- theNumber * theNumber);
+let theNumber = Number(prompt("Pick a number"));
+if (!Number.isNaN(theNumber)) {
+ console.log("Your number is the square root of " +
+ theNumber * theNumber);
+}
```
-The function `Number` converts a value to a number. We need that conversion because the result of `prompt` is a string value, and we want a number. There are similar functions called `String` and `Boolean` that convert values to those types.
-
-Here is the rather trivial schematic representation of straight control flow:
-
-![Trivial control flow](img/controlflow-straight.svg)
-
-## Conditional execution
+With this modification, if you enter “parrot”, no output is shown.
-Executing statements in straight-line order isn't the only option we have. An alternative is _conditional execution_, where we choose between two different routes based on a Boolean value, like this:
+The `if` keyword executes or skips a statement depending on the value of a Boolean expression. The deciding expression is written after the keyword, between parentheses, followed by the statement to execute.
-![Conditional control flow](img/controlflow-if.svg)
+The `Number.isNaN` function is a standard JavaScript function that returns `true` only if the argument it is given is `NaN`. The `Number` function happens to return `NaN` when you give it a string that doesn't represent a valid number. Thus, the condition translates to “unless `theNumber` is not-a-number, do this”.
-Conditional execution is written with the `if` keyword in JavaScript. In the simple case, we just want some code to be executed if, and only if, a certain condition holds. For example, in the previous program, we might want to show the square of the input only if the input is actually a number.
+The statement below the `if` is wrapped in curly braces (`{` and `}`) in this example. Those can be used to group any number of statements into a single statement, called a _block_. You could also have omitted them in this case, since they only hold a single statement, but to avoid having to think about whether they are needed or not, most JavaScript programmers use them in every wrapped statement like this. We'll mostly follow that convention in this book, except for the occasional one-liner.
```
-var theNumber = Number(prompt("Pick a number", ""));
-if (!isNaN(theNumber))
- alert("Your number is the square root of " +
- theNumber * theNumber);
+if (1 + 1 == 2) console.log("It's true");
+// → It's true
```
-With this modification, if you enter “cheese”, no output will be shown.
-
-The keyword `if` executes or skips a statement depending on the value of a Boolean expression. The deciding expression is written after the keyword, between parentheses, followed by the statement to execute.
-
-The `isNaN` function is a standard JavaScript function that returns `true` only if the argument it is given is `NaN`. The `Number` function happens to return `NaN` when you give it a string that doesn't represent a valid number. Thus, the condition translates to “unless `theNumber` is not-a-number, do this”.
-
You often won't just have code that executes when a condition holds true, but also code that handles the other case. This alternate path is represented by the second arrow in the diagram. The `else` keyword can be used, together with `if`, to create two separate, alternative execution paths.
```
-var theNumber = Number(prompt("Pick a number", ""));
-if (!isNaN(theNumber))
- alert("Your number is the square root of " +
- theNumber * theNumber);
-else
- alert("Hey. Why didn't you give me a number?");
+let theNumber = Number(prompt("Pick a number"));
+if (!Number.isNaN(theNumber)) {
+ console.log("Your number is the square root of " +
+ theNumber * theNumber);
+} else {
+ console.log("Hey. Why didn't you give me a number?");
+}
```
If we have more than two paths to choose from, multiple `if`/`else` pairs can be “chained” together. Here's an example:
```
-var num = Number(prompt("Pick a number", "0"));
+let num = Number(prompt("Pick a number"));
-if (num < 10)
- alert("Small");
-else if (num < 100)
- alert("Medium");
-else
- alert("Large");
+if (num < 10) {
+ console.log("Small");
+} else if (num < 100) {
+ console.log("Medium");
+} else {
+ console.log("Large");
+}
```
-The program will first check whether `num` is less than 10\. If it is, it chooses that branch, shows `"Small"`, and is done. If it isn't, it takes the `else` branch, which itself contains a second `if`. If the second condition (`&lt; 100`) holds, that means the number is between 10 and 100, and `"Medium"` is shown. If it doesn't, the second, and last, `else` branch is chosen.
+The program will first check whether `num` is less than 10\. If it is, it chooses that branch, shows `"Small"`, and is done. If it isn't, it takes the `else` branch, which itself contains a second `if`. If the second condition (`&lt; 100`) holds, that means the number is between 10 and 100, and `"Medium"` is shown. If it doesn't, the second and last `else` branch is chosen.
-The flow chart for this program looks something like this:
+The schema for this program looks something like this:
-![Nested if control flow](img/controlflow-nested-if.svg)
+<figure>![Nested if control flow](img/controlflow-nested-if.svg)</figure>
## while and do loops
-Consider a program that prints all even numbers from 0 to 12\. One way to write this is as follows:
+Consider a program that outputs all even numbers from 0 to 12\. One way to write this is as follows:
```
console.log(0);
@@ -247,14 +249,14 @@ console.log(10);
console.log(12);
```
-That works, but the idea of writing a program is to make something _less_ work, not more. If we needed all even numbers less than 1,000, the previous would be unworkable. What we need is a way to repeat some code. This form of control flow is called a _loop_:
+That works, but the idea of writing a program is to make something _less_ work, not more. If we needed all even numbers less than 1,000, this approach would be unworkable. What we need is a way to run a piece of code multiple times. This form of control flow is called a _loop_:
-![Loop control flow](img/controlflow-loop.svg)
+<figure>![Loop control flow](img/controlflow-loop.svg)</figure>
-Looping control flow allows us to go back to some point in the program where we were before and repeat it with our current program state. If we combine this with a variable that counts, we can do something like this:
+Looping control flow allows us to go back to some point in the program where we were before and repeat it with our current program state. If we combine this with a binding that counts, we can do something like this:
```
-var number = 0;
+let number = 0;
while (number <= 12) {
console.log(number);
number = number + 2;
@@ -264,19 +266,15 @@ while (number <= 12) {
// … etcetera
```
-A statement starting with the keyword `while` creates a loop. The word `while` is followed by an expression in parentheses and then a statement, much like `if`. The loop executes that statement as long as the expression produces a value that is `true` when converted to Boolean type.
-
-In this loop, we want to both print the current number and add two to our variable. Whenever we need to execute multiple statements inside a loop, we wrap them in curly braces (`{` and `}`). Braces do for statements what parentheses do for expressions: they group them together, making them count as a single statement. A sequence of statements wrapped in braces is called a _block_.
+A statement starting with the keyword `while` creates a loop. The word `while` is followed by an expression in parentheses and then a statement, much like `if`. The loop keeps entering that statement as long as the expression produces a value that gives `true` when converted to Boolean.
-Many JavaScript programmers wrap every single loop or `if` body in braces. They do this both for the sake of consistency and to avoid having to add or remove braces when changing the number of statements in the body later. In this book, I will write most single-statement bodies without braces, since I value brevity. You are free to go with whichever style you prefer.
+The `number` binding demonstrates the way a binding can track the progress of a program. Every time the loop repeats, `number` gets a value that is 2 more than its previous value. At the beginning of every repetition, it is compared with the number 12 to decide whether the program's work is finished.
-The variable `number` demonstrates the way a variable can track the progress of a program. Every time the loop repeats, `number` is incremented by `2`. Then, at the beginning of every repetition, it is compared with the number `12` to decide whether the program has done all the work it intended to do.
-
-As an example that actually does something useful, we can now write a program that calculates and shows the value of 2&lt;sup&gt;10&lt;/sup&gt; (2 to the 10th power). We use two variables: one to keep track of our result and one to count how often we have multiplied this result by 2\. The loop tests whether the second variable has reached 10 yet and then updates both variables.
+As an example that actually does something useful, we can now write a program that calculates and shows the value of 2&lt;sup&gt;10&lt;/sup&gt; (2 to the 10th power). We use two bindings: one to keep track of our result and one to count how often we have multiplied this result by 2\. The loop tests whether the second binding has reached 10 yet and, if not, updates both bindings.
```
-var result = 1;
-var counter = 0;
+let result = 1;
+let counter = 0;
while (counter < 10) {
result = result * 2;
counter = counter + 1;
@@ -285,65 +283,80 @@ console.log(result);
// → 1024
```
-The counter could also start at `1` and check for `&lt;= 10`, but, for reasons that will become apparent in [Chapter 4](04_data.html#array_indexing), it is a good idea to get used to counting from 0.
+The counter could also have started at `1` and checked for `&lt;= 10`, but, for reasons that will become apparent in [Chapter 4](04_data.html#array_indexing), it is a good idea to get used to counting from 0.
-The `do` loop is a control structure similar to the `while` loop. It differs only on one point: a `do` loop always executes its body at least once, and it starts testing whether it should stop only after that first execution. To reflect this, the test appears after the body of the loop:
+A `do` loop is a control structure similar to a `while` loop. It differs only on one point: a `do` loop always executes its body at least once, and it starts testing whether it should stop only after that first execution. To reflect this, the test appears after the body of the loop:
```
+let yourName;
do {
- var yourName = prompt("Who are you?");
+ yourName = prompt("Who are you?");
} while (!yourName);
console.log(yourName);
```
-This program will force you to enter a name. It will ask again and again until it gets something that is not an empty string. Applying the `!` operator will convert a value to Boolean type before negating it, and all strings except `""` convert to `true`. This means the loop continues going round until you provide a name that is not the empty string.
+This program will force you to enter a name. It will ask again and again until it gets something that is not an empty string. Applying the `!` operator will convert a value to Boolean type before negating it, and all strings except `""` convert to `true`. This means the loop continues going round until you provide a non-empty name.
## Indenting Code
-You've probably noticed the spaces I put in front of some statements. In JavaScript, these are not required—the computer will accept the program just fine without them. In fact, even the line breaks in programs are optional. You could write a program as a single long line if you felt like it. The role of the indentation inside blocks is to make the structure of the code stand out. In complex code, where new blocks are opened inside other blocks, it can become hard to see where one block ends and another begins. With proper indentation, the visual shape of a program corresponds to the shape of the blocks inside it. I like to use two spaces for every open block, but tastes differ—some people use four spaces, and some people use tab characters.
+In the examples, I've been adding spaces in front of statements that are part of some larger statement. These are not required—the computer will accept the program just fine without them. In fact, even the line breaks in programs are optional. You could write a program as a single long line if you felt like it.
+
+The role of this indentation inside blocks is to make the structure of the code stand out. In code where new blocks are opened inside other blocks, it can become hard to see where one block ends and another begins. With proper indentation, the visual shape of a program corresponds to the shape of the blocks inside it. I like to use two spaces for every open block, but tastes differ—some people use four spaces, and some people use tab characters. The important thing is that each new block adds the same amount of space.
+
+```
+if (false != true) {
+ console.log("That makes sense.");
+ if (1 < 2) {
+ console.log("No surprise there.");
+ }
+}
+```
+
+Most code editor programs (including the one in this book) will help by automatically indenting new lines the proper amount.
## for loops
-Many loops follow the pattern seen in the previous `while` examples. First, a “counter” variable is created to track the progress of the loop. Then comes a `while` loop, whose test expression usually checks whether the counter has reached some boundary yet. At the end of the loop body, the counter is updated to track progress.
+Many loops follow the pattern seen in the `while` examples. First, a “counter” binding is created to track the progress of the loop. Then comes a `while` loop, usually with a test expression that checks whether the counter has reached its end value. At the end of the loop body, the counter is updated to track progress.
-Because this pattern is so common, JavaScript and similar languages provide a slightly shorter and more comprehensive form, the `for` loop.
+Because this pattern is so common, JavaScript and similar languages provide a slightly shorter and more comprehensive form, the `for` loop:
```
-for (var number = 0; number <= 12; number = number + 2)
+for (let number = 0; number <= 12; number = number + 2) {
console.log(number);
+}
// → 0
// → 2
// … etcetera
```
-This program is exactly equivalent to the [earlier](02_program_structure.html#loops) even-number-printing example. The only change is that all the statements that are related to the “state” of the loop are now grouped together.
+This program is exactly equivalent to the [earlier](02_program_structure.html#loops) even-number-printing example. The only change is that all the statements that are related to the “state” of the loop are grouped together after `for`.
-The parentheses after a `for` keyword must contain two semicolons. The part before the first semicolon _initializes_ the loop, usually by defining a variable. The second part is the expression that _checks_ whether the loop must continue. The final part _updates_ the state of the loop after every iteration. In most cases, this is shorter and clearer than a `while` construct.
+The parentheses after a `for` keyword must contain two semicolons. The part before the first semicolon _initializes_ the loop, usually by defining a binding. The second part is the expression that _checks_ whether the loop must continue. The final part _updates_ the state of the loop after every iteration. In most cases, this is shorter and clearer than a `while` construct.
-Here is the code that computes 2&lt;sup&gt;10&lt;/sup&gt;, using `for` instead of `while`:
+This is the code that computes 2&lt;sup&gt;10&lt;/sup&gt;, using `for` instead of `while`:
```
-var result = 1;
-for (var counter = 0; counter < 10; counter = counter + 1)
+let result = 1;
+for (let counter = 0; counter < 10; counter = counter + 1) {
result = result * 2;
+}
console.log(result);
// → 1024
```
-Note that even though no block is opened with a `{`, the statement in the loop is still indented two spaces to make it clear that it “belongs” to the line before it.
-
## Breaking Out of a Loop
-Having the loop's condition produce `false` is not the only way a loop can finish. There is a special statement called `break` that has the effect of immediately jumping out of the enclosing loop.
+Having the looping condition produce `false` is not the only way a loop can finish. There is a special statement called `break` that has the effect of immediately jumping out of the enclosing loop.
This program illustrates the `break` statement. It finds the first number that is both greater than or equal to 20 and divisible by 7.
```
-for (var current = 20; ; current++) {
- if (current % 7 == 0)
+for (let current = 20; ; current = current + 1) {
+ if (current % 7 == 0) {
+ console.log(current);
break;
+ }
}
-console.log(current);
// → 21
```
@@ -351,15 +364,15 @@ Using the remainder (`%`) operator is an easy way to test whether a number is di
The `for` construct in the example does not have a part that checks for the end of the loop. This means that the loop will never stop unless the `break` statement inside is executed.
-If you were to leave out that `break` statement or accidentally write a condition that always produces `true`, your program would get stuck in an _infinite loop_. A program stuck in an infinite loop will never finish running, which is usually a bad thing.
+If you were to remove that `break` statement or you accidentally write an end condition that always produces `true`, your program would get stuck in an _infinite loop_. A program stuck in an infinite loop will never finish running, which is usually a bad thing.
If you create an infinite loop in one of the examples on these pages, you'll usually be asked whether you want to stop the script after a few seconds. If that fails, you will have to close the tab that you're working in, or on some browsers close your whole browser, in order to recover.
The `continue` keyword is similar to `break`, in that it influences the progress of a loop. When `continue` is encountered in a loop body, control jumps out of the body and continues with the loop's next iteration.
-## Updating variables succinctly
+## Updating bindings succinctly
-Especially when looping, a program often needs to “update” a variable to hold a value based on that variable's previous value.
+Especially when looping, a program often needs to “update” a binding to hold a value based on that binding's previous value.
```
counter = counter + 1;
@@ -376,24 +389,25 @@ Similar shortcuts work for many other operators, such as `result *= 2` to double
This allows us to shorten our counting example a little more.
```
-for (var number = 0; number <= 12; number += 2)
+for (let number = 0; number <= 12; number += 2) {
console.log(number);
+}
```
For `counter += 1` and `counter -= 1`, there are even shorter equivalents: `counter++` and `counter--`.
## Dispatching on a value with switch
-It is common for code to look like this:
+It is not uncommon for code to look like this:
```
-if (variable == "value1") action1();
-else if (variable == "value2") action2();
-else if (variable == "value3") action3();
+if (x == "value1") action1();
+else if (x == "value2") action2();
+else if (x == "value3") action3();
else defaultAction();
```
-There is a construct called `switch` that is intended to solve such a “dispatch” in a more direct way. Unfortunately, the syntax JavaScript uses for this (which it inherited from the C/Java line of programming languages) is somewhat awkward—a chain of `if` statements often looks better. Here is an example:
+There is a construct called `switch` that is intended to express such a “dispatch” in a more direct way. Unfortunately, the syntax JavaScript uses for this (which it inherited from the C/Java line of programming languages) is somewhat awkward—a chain of `if` statements may look better. Here is an example:
```
switch (prompt("What is the weather like?")) {
@@ -411,11 +425,11 @@ switch (prompt("What is the weather like?")) {
}
```
-You may put any number of `case` labels inside the block opened by `switch`. The program will jump to the label that corresponds to the value that `switch` was given or to `default` if no matching value is found. It starts executing statements there, even if they're under another label, until it reaches a `break` statement. In some cases, such as the `"sunny"` case in the example, this can be used to share some code between cases (it recommends going outside for both sunny and cloudy weather). But beware: it is easy to forget such a `break`, which will cause the program to execute code you do not want executed.
+You may put any number of `case` labels inside the block opened by `switch`. The program will start executing at the label that corresponds to the value that `switch` was given, or at `default` if no matching value is found. It will continue executing, even across other labels, until it reaches a `break` statement. In some cases, such as the `"sunny"` case in the example, this can be used to share some code between cases (it recommends going outside for both sunny and cloudy weather). But be careful—it is easy to forget such a `break`, which will cause the program to execute code you do not want executed.
## Capitalization
-Variable names may not contain spaces, yet it is often helpful to use multiple words to clearly describe what the variable represents. These are pretty much your choices for writing a variable name with several words in it:
+Binding names may not contain spaces, yet it is often helpful to use multiple words to clearly describe what the binding represents. These are pretty much your choices for writing a binding name with several words in it:
```
fuzzylittleturtle
@@ -424,38 +438,38 @@ FuzzyLittleTurtle
fuzzyLittleTurtle
```
-The first style can be hard to read. Personally, I like the look of the underscores, though that style is a little painful to type. The standard JavaScript functions, and most JavaScript programmers, follow the bottom style—they capitalize every word except the first. It is not hard to get used to little things like that, and code with mixed naming styles can be jarring to read, so we will just follow this convention.
+The first style can be hard to read. I rather like the look of the underscores, though that style is a little painful to type. The standard JavaScript functions, and most JavaScript programmers, follow the bottom style—they capitalize every word except the first. It is not hard to get used to little things like that, and code with mixed naming styles can be jarring to read, so we follow this convention.
-In a few cases, such as the `Number` function, the first letter of a variable is also capitalized. This was done to mark this function as a constructor. What a constructor is will become clear in [Chapter 6](06_object.html#constructors). For now, the important thing is not to be bothered by this apparent lack of consistency.
+In a few cases, such as the `Number` function, the first letter of a binding is also capitalized. This was done to mark this function as a constructor. What a constructor is will become clear in [Chapter 6](06_object.html#constructors). For now, the important thing is not to be bothered by this apparent lack of consistency.
## Comments
-Often, raw code does not convey all the information you want a program to convey to human readers, or it conveys it in such a cryptic way that people might not understand it. At other times, you might just feel poetic or want to include some thoughts as part of your program. This is what _comments_ are for.
+Often, raw code does not convey all the information you want a program to convey to human readers, or it conveys it in such a cryptic way that people might not understand it. At other times, you might just want to include some related thoughts as part of your program. This is what _comments_ are for.
A comment is a piece of text that is part of a program but is completely ignored by the computer. JavaScript has two ways of writing comments. To write a single-line comment, you can use two slash characters (`//`) and then the comment text after it.
```
-var accountBalance = calculateBalance(account);
+let accountBalance = calculateBalance(account);
// It's a green hollow where a river sings
accountBalance.adjust();
// Madly catching white tatters in the grass.
-var report = new Report();
+let report = new Report();
// Where the sun on the proud mountain rings:
addToReport(accountBalance, report);
// It's a little valley, foaming like light in a glass.
```
-A `//` comment goes only to the end of the line. A section of text between `/*` and `*/` will be ignored, regardless of whether it contains line breaks. This is often useful for adding blocks of information about a file or a chunk of program.
+A `//` comment goes only to the end of the line. A section of text between `/*` and `*/` will be ignored in its entirety, regardless of whether it contains line breaks. This is useful for adding blocks of information about a file or a chunk of program.
```
/*
I first found this number scrawled on the back of one of
- my notebooks a few years ago. Since then, it has often
- dropped by, showing up in phone numbers and the serial
- numbers of products that I've bought. It obviously likes
- me, so I've decided to keep it.
+ an old notebook. Since then, it has often dropped by,
+ showing up in phone numbers and the serial numbers of
+ products that I've bought. It obviously likes me, so I've
+ decided to keep it.
*/
-var myNumber = 11213;
+const myNumber = 11213;
```
## Summary
@@ -464,15 +478,15 @@ You now know that a program is built out of statements, which themselves sometim
Putting statements after one another gives you a program that is executed from top to bottom. You can introduce disturbances in the flow of control by using conditional (`if`, `else`, and `switch`) and looping (`while`, `do`, and `for`) statements.
-Variables can be used to file pieces of data under a name, and they are useful for tracking state in your program. The environment is the set of variables that are defined. JavaScript systems always put a number of useful standard variables into your environment.
+Bindings can be used to file pieces of data under a name, and they are useful for tracking state in your program. The environment is the set of bindings that are defined. JavaScript systems always put a number of useful standard bindings into your environment.
Functions are special values that encapsulate a piece of program. You can invoke them by writing `functionName(argument1, argument2)`. Such a function call is an expression, and may produce a value.
## Exercises
-If you are unsure how to try your solutions to exercises, refer to the [introduction](00_intro.html#intro).
+If you are unsure how to try your solutions to exercises, refer to the [introduction](00_intro.html).
-Each exercise starts with a problem description. Read that and try to solve the exercise. If you run into problems, consider reading the hints after the exercise. Full solutions to the exercises are not included in this book, but you can find them online at [_eloquentjavascript.net/code_](http://eloquentjavascript.net/2nd_edition/code). If you want to learn something from the exercises, I recommend looking at the solutions only after you've solved the exercise, or at least after you've attacked it long and hard enough to have a slight headache.
+Each exercise starts with a problem description. Read that and try to solve the exercise. If you run into problems, consider reading the hints after the exercise. Full solutions to the exercises are not included in this book, but you can find them online at [_eloquentjavascript.net/code_](https://eloquentjavascript.net/code#2). If you want to learn something from the exercises, I recommend looking at the solutions only after you've solved the exercise, or at least after you've attacked it long and hard enough to have a slight headache.
### Looping a triangle
@@ -488,10 +502,10 @@ Write a loop that makes seven calls to `console.log` to output the following tri
#######
```
-It may be useful to know that you can find the length of a string by writing `.length` after it.
+It may be useful to know that you can find the length of a string by writing `.length` after it:
```
-var abc = "abc";
+let abc = "abc";
console.log(abc.length);
// → 3
```
@@ -502,7 +516,7 @@ Most exercises contain a piece of code that you can modify to solve the exercise
// Your code here.
```
-You can start with a program that simply prints out the numbers 1 to 7, which you can derive by making a few modifications to the [even number printing example](02_program_structure.html#loops) given earlier in the chapter, where the `for` loop was introduced.
+You can start with a program that prints out the numbers 1 to 7, which you can derive by making a few modifications to the [even number printing example](02_program_structure.html#loops) given earlier in the chapter, where the `for` loop was introduced.
Now consider the equivalence between numbers and strings of hash characters. You can go from 1 to 2 by adding 1 (`+= 1`). You can go from `"#"` to `"##"` by adding a character (`+= "#"`). Thus, your solution can closely follow the number-printing program.
@@ -512,7 +526,7 @@ Write a program that uses `console.log` to print all the numbers from 1 to 100,
When you have that working, modify your program to print `"FizzBuzz"`, for numbers that are divisible by both 3 and 5 (and still print `"Fizz"` or `"Buzz"` for numbers divisible by only one of those).
-(This is actually an interview question that has been claimed to weed out a significant percentage of programmer candidates. So if you solved it, you're now allowed to feel good about yourself.)
+(This is actually an interview question that has been claimed to weed out a significant percentage of programmer candidates. So if you solved it, your labor market value just went up.)
```
// Your code here.
@@ -522,26 +536,26 @@ Going over the numbers is clearly a looping job, and selecting what to print is
In the first version, there are three possible outcomes for every number, so you'll have to create an `if`/`else if`/`else` chain.
-The second version of the program has a straightforward solution and a clever one. The simple way is to add another “branch” to precisely test the given condition. For the clever method, build up a string containing the word or words to output, and print either this word or the number if there is no word, potentially by making elegant use of the `||` operator.
+The second version of the program has a straightforward solution and a clever one. The simple way is to add another conditional “branch” to precisely test the given condition. For the clever method, build up a string containing the word or words to output and print either this word or the number if there is no word, potentially by making good use of the `||` operator.
### Chess board
-Write a program that creates a string that represents an 8×8 grid, using newline characters to separate lines. At each position of the grid there is either a space or a “#” character. The characters should form a chess board.
+Write a program that creates a string that represents an 8×8 grid, using newline characters to separate lines. At each position of the grid there is either a space or a "#" character. The characters should form a chess board.
Passing this string to `console.log` should show something like this:
```
# # # #
-# # # #
+# # # #
# # # #
-# # # #
+# # # #
# # # #
-# # # #
+# # # #
# # # #
# # # #
```
-When you have a program that generates this pattern, define a variable `size = 8` and change the program so that it works for any `size`, outputting a grid of the given width and height.
+When you have a program that generates this pattern, define a binding `size = 8` and change the program so that it works for any `size`, outputting a grid of the given width and height.
```
// Your code here.
@@ -549,10 +563,8 @@ When you have a program that generates this pattern, define a variable `size = 8
The string can be built by starting with an empty one (`""`) and repeatedly adding characters. A newline character is written `"\n"`.
-Use `console.log` to inspect the output of your program.
-
To work with two dimensions, you will need a loop inside of a loop. Put curly braces around the bodies of both loops to make it easy to see where they start and end. Try to properly indent these bodies. The order of the loops must follow the order in which we build up the string (line by line, left to right, top to bottom). So the outer loop handles the lines and the inner loop handles the characters on a line.
-You'll need two variables to track your progress. To know whether to put a space or a hash sign at a given position, you could test whether the sum of the two counters is even (`% 2`).
+You'll need two bindings to track your progress. To know whether to put a space or a hash sign at a given position, you could test whether the sum of the two counters is even (`% 2`).
-Terminating a line by adding a newline character happens after the line has been built up, so do this after the inner loop but inside of the outer loop.
+Terminating a line by adding a newline character must happen after the line has been built up, so do this after the inner loop but inside of the outer loop.

727
diff-en/2ech3-3ech3.diff Normal file
View File

@ -0,0 +1,727 @@
diff --git a/2ech3.md b/3ech3.md
index e6afba9..4dd4357 100644
--- a/2ech3.md
+++ b/3ech3.md
@@ -4,18 +4,18 @@
>
> &lt;footer&gt;Donald Knuth&lt;/footer&gt;
-You've seen function values, such as `alert`, and how to call them. Functions are the bread and butter of JavaScript programming. The concept of wrapping a piece of program in a value has many uses. It is a tool to structure larger programs, to reduce repetition, to associate names with subprograms, and to isolate these subprograms from each other.
+Functions are the bread and butter of JavaScript programming. The concept of wrapping a piece of program in a value has many uses. It gives us a way to structure larger programs, to reduce repetition, to associate names with subprograms, and to isolate these subprograms from each other.
-The most obvious application of functions is defining new vocabulary. Creating new words in regular, human-language prose is usually bad style. But in programming, it is indispensable.
+The most obvious application of functions is defining new vocabulary. Creating new words in prose is usually bad style. But in programming, it is indispensable.
-Typical adult English speakers have some 20,000 words in their vocabulary. Few programming languages come with 20,000 commands built in. And the vocabulary that _is_ available tends to be more precisely defined, and thus less flexible, than in human language. Therefore, we usually _have_ to add some of our own vocabulary to avoid repeating ourselves too much.
+Typical adult English speakers have some 20,000 words in their vocabulary. Few programming languages come with 20,000 commands built in. And the vocabulary that _is_ available tends to be more precisely defined, and thus less flexible, than in human language. Therefore, we usually _have_ to introduce new concepts to avoid repeating ourselves too much.
## Defining a function
-A function definition is just a regular variable definition where the value given to the variable happens to be a function. For example, the following code defines the variable `square` to refer to a function that produces the square of a given number:
+A function definition is a regular binding where the value of the binding is a function. For example, this code defines `square` to refer to a function that produces the square of a given number:
```
-var square = function(x) {
+const square = function(x) {
return x * x;
};
@@ -23,22 +23,23 @@ console.log(square(12));
// → 144
```
-A function is created by an expression that starts with the keyword `function`. Functions have a set of _parameters_ (in this case, only `x`) and a _body_, which contains the statements that are to be executed when the function is called. The function body must always be wrapped in braces, even when it consists of only a single statement (as in the previous example).
+A function is created with an expression that starts with the keyword `function`. Functions have a set of _parameters_ (in this case, only `x`) and a _body_, which contains the statements that are to be executed when the function is called. The function body of a function created this way must always be wrapped in braces, even when it consists of only a single statement.
A function can have multiple parameters or no parameters at all. In the following example, `makeNoise` does not list any parameter names, whereas `power` lists two:
```
-var makeNoise = function() {
+const makeNoise = function() {
console.log("Pling!");
};
makeNoise();
// → Pling!
-var power = function(base, exponent) {
- var result = 1;
- for (var count = 0; count < exponent; count++)
+const power = function(base, exponent) {
+ let result = 1;
+ for (let count = 0; count < exponent; count++) {
result *= base;
+ }
return result;
};
@@ -46,108 +47,93 @@ console.log(power(2, 10));
// → 1024
```
-Some functions produce a value, such as `power` and `square`, and some don't, such as `makeNoise`, which produces only a side effect. A `return` statement determines the value the function returns. When control comes across such a statement, it immediately jumps out of the current function and gives the returned value to the code that called the function. The `return` keyword without an expression after it will cause the function to return `undefined`.
+Some functions produce a value, such as `power` and `square`, and some don't, such as `makeNoise`, whose only result is a side effect. A `return` statement determines the value the function returns. When control comes across such a statement, it immediately jumps out of the current function and gives the returned value to the code that called the function. A `return` keyword without an expression after it will cause the function to return `undefined`. Functions that don't have a `return` statement at all, such as `makeNoise`, similarly return `undefined`.
-## Parameters and scopes
+Parameters to a function behave like regular bindings, but their initial values are given by the _caller_ of the function, not the code in the function itself.
-The parameters to a function behave like regular variables, but their initial values are given by the _caller_ of the function, not the code in the function itself.
+## Bindings and scopes
-An important property of functions is that the variables created inside of them, including their parameters, are _local_ to the function. This means, for example, that the `result` variable in the `power` example will be newly created every time the function is called, and these separate incarnations do not interfere with each other.
+Each binding has a _scope_, which is the part of the program in which the binding is visible. For bindings defined outside of any function or block, the scope is the whole program—you can refer to such bindings wherever you want. These are called _global_.
-This “localness” of variables applies only to the parameters and to variables declared with the `var` keyword inside the function body. Variables declared outside of any function are called _global_, because they are visible throughout the program. It is possible to access such variables from inside a function, as long as you haven't declared a local variable with the same name.
+But bindings created for function parameters or declared inside a function can only be referenced in that function, so they are known as _local_ bindings. Every time the function is called, new instances of these bindings are created. This provides some isolation between functions—each function call acts in its own little world (its local environment) and can often be understood without knowing a lot about what's going on in the global environment.
-The following code demonstrates this. It defines and calls two functions that both assign a value to the variable `x`. The first one declares the variable as local and thus changes only the local variable. The second does not declare `x` locally, so references to `x` inside of it refer to the global variable `x` defined at the top of the example.
+Bindings declared with `let` and `const` are in fact local to the _block_ that they are declared in, so if you create one of those inside of a loop, the code before and after the loop cannot “see” it. In pre-2015 JavaScript, only functions created new scopes, so old-style bindings, created with the `var` keyword, are visible throughout the whole function that they appear in—or throughout the global scope, if they are not in a function.
```
-var x = "outside";
-
-var f1 = function() {
- var x = "inside f1";
-};
-f1();
-console.log(x);
-// → outside
-
-var f2 = function() {
- x = "inside f2";
-};
-f2();
-console.log(x);
-// → inside f2
+let x = 10;
+if (true) {
+ let y = 20;
+ var z = 30;
+ console.log(x + y + z);
+ // → 60
+}
+// y is not visible here
+console.log(x + z);
+// → 40
```
-This behavior helps prevent accidental interference between functions. If all variables were shared by the whole program, it'd take a lot of effort to make sure no name is ever used for two different purposes. And if you _did_ reuse a variable name, you might see strange effects from unrelated code messing with the value of your variable. By treating function-local variables as existing only within the function, the language makes it possible to read and understand functions as small universes, without having to worry about all the code at once.
-
-## Nested scope
-
-JavaScript distinguishes not just between _global_ and _local_ variables. Functions can be created inside other functions, producing several degrees of locality.
-
-For example, this rather nonsensical function has two functions inside of it:
+Each scope can “look out” into the scope around it, so `x` is visible inside the block in the example. The exception is when multiple bindings have the same name—in that case, code can only see the innermost one. For example, when the code inside the `halve` function refers to `n`, it is seeing its _own_ `n`, not the global `n`.
```
-var landscape = function() {
- var result = "";
- var flat = function(size) {
- for (var count = 0; count < size; count++)
- result += "_";
- };
- var mountain = function(size) {
- result += "/";
- for (var count = 0; count < size; count++)
- result += "'";
- result += "\\";
- };
-
- flat(3);
- mountain(4);
- flat(6);
- mountain(1);
- flat(1);
- return result;
+const halve = function(n) {
+ return n / 2;
};
-console.log(landscape());
-// → ___/''''\______/'\_
+let n = 10;
+console.log(halve(100));
+// → 50
+console.log(n);
+// → 10
```
-The `flat` and `mountain` functions can “see” the variable called `result`, since they are inside the function that defines it. But they cannot see each other's `count` variables since they are outside each other's scope. The environment outside of the `landscape` function doesn't see any of the variables defined inside `landscape`.
+### Nested scope
-In short, each local scope can also see all the local scopes that contain it. The set of variables visible inside a function is determined by the place of that function in the program text. All variables from blocks _around_ a function's definition are visible—meaning both those in function bodies that enclose it and those at the top level of the program. This approach to variable visibility is called _lexical scoping_.
+JavaScript distinguishes not just _global_ and _local_ bindings. Blocks and functions can be created inside other blocks and functions, producing multiple degrees of locality.
-People who have experience with other programming languages might expect that any block of code between braces produces a new local environment. But in JavaScript, functions are the only things that create a new scope. You are allowed to use free-standing blocks.
+For example, this function—which outputs the ingredients needed to make a batch of hummus—has another function inside it:
```
-var something = 1;
-{
- var something = 2;
- // Do stuff with variable something...
-}
-// Outside of the block again...
+const hummus = function(factor) {
+ const ingredient = function(amount, unit, name) {
+ let ingredientAmount = amount * factor;
+ if (ingredientAmount > 1) {
+ unit += "s";
+ }
+ console.log(`${ingredientAmount} ${unit} ${name}`);
+ };
+ ingredient(1, "can", "chickpeas");
+ ingredient(0.25, "cup", "tahini");
+ ingredient(0.25, "cup", "lemon juice");
+ ingredient(1, "clove", "garlic");
+ ingredient(2, "tablespoon", "olive oil");
+ ingredient(0.5, "teaspoon", "cumin");
+};
```
-But the `something` inside the block refers to the same variable as the one outside the block. In fact, although blocks like this are allowed, they are useful only to group the body of an `if` statement or a loop.
+The code inside the `ingredient` function can see the `factor` binding from the outer function. But its local bindings, such as `unit` or `ingredientAmount`, are not visible in the outer function.
-If you find this odd, you're not alone. The next version of JavaScript will introduce a `let` keyword, which works like `var` but creates a variable that is local to the enclosing _block_, not the enclosing _function_.
+In short, each local scope can also see all the local scopes that contain it. The set of bindings visible inside a block is determined by the place of that block in the program text. Each local scope can also see all the local scopes that contain it, and all scopes can see the global scope. This approach to binding visibility is called _lexical scoping_.
## Functions as values
-Function variables usually simply act as names for a specific piece of the program. Such a variable is defined once and never changed. This makes it easy to start confusing the function and its name.
+A function binding usually simply acts as a name for a specific piece of the program. Such a binding is defined once and never changed. This makes it easy to confuse the function and its name.
-But the two are different. A function value can do all the things that other values can do—you can use it in arbitrary expressions, not just call it. It is possible to store a function value in a new place, pass it as an argument to a function, and so on. Similarly, a variable that holds a function is still just a regular variable and can be assigned a new value, like so:
+But the two are different. A function value can do all the things that other values can do—you can use it in arbitrary expressions, not just call it. It is possible to store a function value in a new binding, pass it as an argument to a function, and so on. Similarly, a binding that holds a function is still just a regular binding and can, if not constant, be assigned a new value, like so:
```
-var launchMissiles = function(value) {
+let launchMissiles = function() {
missileSystem.launch("now");
};
-if (safeMode)
- launchMissiles = function(value) {/* do nothing */};
+if (safeMode) {
+ launchMissiles = function() {/* do nothing */};
+}
```
-In [Chapter 5](05_higher_order.html#higher_order), we will discuss the wonderful things that can be done by passing around function values to other functions.
+In [Chapter 5](05_higher_order.html), we will discuss the interesting things that can be done by passing around function values to other functions.
## Declaration notation
-There is a slightly shorter way to say “`var square = function…`”. The `function` keyword can also be used at the start of a statement, as in the following:
+There is a slightly shorter way to create a function binding. When the `function` keyword is used at the start of a statement, it works differently.
```
function square(x) {
@@ -155,32 +141,56 @@ function square(x) {
}
```
-This is a function _declaration_. The statement defines the variable `square` and points it at the given function. So far so good. There is one subtlety with this form of function definition, however.
+This is a function _declaration_. The statement defines the binding `square` and points it at the given function. It is slightly easier to write and doesn't require a semicolon after the function.
+
+There is one subtlety with this form of function definition.
```
console.log("The future says:", future());
function future() {
- return "We STILL have no flying cars.";
+ return "You'll never have flying cars";
}
```
-This code works, even though the function is defined _below_ the code that uses it. This is because function declarations are not part of the regular top-to-bottom flow of control. They are conceptually moved to the top of their scope and can be used by all the code in that scope. This is sometimes useful because it gives us the freedom to order code in a way that seems meaningful, without worrying about having to define all functions above their first use.
+The preceding code works, even though the function is defined _below_ the code that uses it. Function declarations are not part of the regular top-to-bottom flow of control. They are conceptually moved to the top of their scope and can be used by all the code in that scope. This is sometimes useful because it offers the freedom to order code in a way that seems meaningful, without worrying about having to define all functions before they are used.
+
+## Arrow functions
-What happens when you put such a function definition inside a conditional (`if`) block or a loop? Well, don't do that. Different JavaScript platforms in different browsers have traditionally done different things in that situation, and the latest standard actually forbids it. If you want your programs to behave consistently, only use this form of function-defining statements in the outermost block of a function or program.
+There's a third notation for functions, which looks very different from the others. Instead of the `function` keyword, it uses an arrow (`=&gt;`) made up of equals and greater-than characters (not to be confused with the greater-than-or-equal operator, which is written `&gt;=`).
```
-function example() {
- function a() {} // Okay
- if (something) {
- function b() {} // Danger!
+const power = (base, exponent) => {
+ let result = 1;
+ for (let count = 0; count < exponent; count++) {
+ result *= base;
}
-}
+ return result;
+};
```
+The arrow comes _after_ the list of parameters and is followed by the function's body. It expresses something like “this input (the parameters) produces this result (the body)”.
+
+When there is only one parameter name, you can omit the parentheses around the parameter list. If the body is a single expression, rather than a block in braces, that expression will be returned from the function. So these two definitions of `square` do the same thing:
+
+```
+const square1 = (x) => { return x * x; };
+const square2 = x => x * x;
+```
+
+When an arrow function has no parameters at all, its parameter list is just an empty set of parentheses.
+
+```
+const horn = () => {
+ console.log("Toot");
+};
+```
+
+There's no very good reason to have both arrow functions and `function` expressions in the language. Apart from a minor detail, which we'll discuss in [Chapter 6](06_object.html), they do the same thing. Arrow functions were added in 2015, mostly to make it possible to write small function expressions in a less verbose way. We'll be using them a lot in [Chapter 5](05_higher_order.html).
+
## The call stack
-It will be helpful to take a closer look at the way control flows through functions. Here is a simple program that makes a few function calls:
+The way control flows through functions is somewhat involved. Let's take a closer look at it. Here is a simple program that makes a few function calls:
```
function greet(who) {
@@ -190,25 +200,25 @@ greet("Harry");
console.log("Bye");
```
-A run through this program goes roughly like this: the call to `greet` causes control to jump to the start of that function (line 2). It calls `console.log` (a built-in browser function), which takes control, does its job, and then returns control to line 2\. Then it reaches the end of the `greet` function, so it returns to the place that called it, at line 4\. The line after that calls `console.log` again.
+A run through this program goes roughly like this: the call to `greet` causes control to jump to the start of that function (line 2). The function calls `console.log`, which takes control, does its job, and then returns control to line 2\. There it reaches the end of the `greet` function, so it returns to the place that called it, which is line 4\. The line after that calls `console.log` again. After that returns, the program reaches its end.
We could show the flow of control schematically like this:
```
-top
- greet
- console.log
- greet
-top
- console.log
-top
+not in function
+ in greet
+ in console.log
+ in greet
+not in function
+ in console.log
+not in function
```
-Because a function has to jump back to the place of the call when it returns, the computer must remember the context from which the function was called. In one case, `console.log` has to jump back to the `greet` function. In the other case, it jumps back to the end of the program.
+Because a function has to jump back to the place that called it when it returns, the computer must remember the context from which the call happened. In one case, `console.log` has to return to the `greet` function when it is done. In the other case, it returns to the end of the program.
-The place where the computer stores this context is the _call stack_. Every time a function is called, the current context is put on top of this “stack”. When the function returns, it removes the top context from the stack and uses it to continue execution.
+The place where the computer stores this context is the _call stack_. Every time a function is called, the current context is stored on top of this stack. When a function returns, it removes the top context from the stack and uses that context to continue execution.
-Storing this stack requires space in the computer's memory. When the stack grows too big, the computer will fail with a message like “out of stack space” or “too much recursion”. The following code illustrates this by asking the computer a really hard question, which causes an infinite back-and-forth between two functions. Rather, it _would_ be infinite, if the computer had an infinite stack. As it is, we will run out of space, or “blow the stack”.
+Storing this stack requires space in the computer's memory. When the stack grows too big, the computer will fail with a message like “out of stack space” or “too much recursion”. The following code illustrates this by asking the computer a really hard question that causes an infinite back-and-forth between two functions. Rather, it _would_ be infinite, if the computer had an infinite stack. As it is, we will run out of space, or “blow the stack”.
```
function chicken() {
@@ -226,130 +236,147 @@ console.log(chicken() + " came first.");
The following code is allowed and executes without any problem:
```
-alert("Hello", "Good Evening", "How do you do?");
+function square(x) { return x * x; }
+console.log(square(4, true, "hedgehog"));
+// → 16
```
-The function `alert` officially accepts only one argument. Yet when you call it like this, it doesn't complain. It simply ignores the other arguments and shows you “Hello”.
+We defined `square` with only one parameter. Yet when we call it with three, the language doesn't complain. It ignores the extra arguments and computes the square of the first one.
-JavaScript is extremely broad-minded about the number of arguments you pass to a function. If you pass too many, the extra ones are ignored. If you pass too few, the missing parameters simply get assigned the value `undefined`.
+JavaScript is extremely broad-minded about the number of arguments you pass to a function. If you pass too many, the extra ones are ignored. If you pass too few, the missing parameters get assigned the value `undefined`.
-The downside of this is that it is possible—likely, even—that you'll accidentally pass the wrong number of arguments to functions and no one will tell you about it.
+The downside of this is that it is possible—likely, even—that you'll accidentally pass the wrong number of arguments to functions. And no one will tell you about it.
-The upside is that this behavior can be used to have a function take “optional” arguments. For example, the following version of `power` can be called either with two arguments or with a single argument, in which case the exponent is assumed to be two, and the function behaves like `square`.
+The upside is that this behavior can be used to allow a function to be called with different amounts of arguments. For example, this `minus` function tries to imitate the `-` operator by acting on either one or two arguments:
```
-function power(base, exponent) {
- if (exponent == undefined)
- exponent = 2;
- var result = 1;
- for (var count = 0; count < exponent; count++)
+function minus(a, b) {
+ if (b === undefined) return -a;
+ else return a - b;
+}
+
+console.log(minus(10));
+// → -10
+console.log(minus(10, 5));
+// → 5
+```
+
+If you write an `=` operator after a parameter, followed by an expression, the value of that expression will replace the argument when it is not given.
+
+For example, this version of `power` makes its second argument optional. If you don't provide it or pass the value `undefined`, it will default to two, and the function will behave like `square`.
+
+```
+function power(base, exponent = 2) {
+ let result = 1;
+ for (let count = 0; count < exponent; count++) {
result *= base;
+ }
return result;
}
console.log(power(4));
// → 16
-console.log(power(4, 3));
+console.log(power(2, 6));
// → 64
```
-In the [next chapter](04_data.html#arguments_object), we will see a way in which a function body can get at the exact list of arguments that were passed. This is helpful because it makes it possible for a function to accept any number of arguments. For example, `console.log` makes use of this—it outputs all of the values it is given.
+In the [next chapter](04_data.html#rest_parameters), we will see a way in which a function body can get at the whole list of arguments it was passed. This is helpful because it makes it possible for a function to accept any number of arguments. For example, `console.log` does this—it outputs all of the values it is given.
```
-console.log("R", 2, "D", 2);
-// → R 2 D 2
+console.log("C", "O", 2);
+// → C O 2
```
## Closure
-The ability to treat functions as values, combined with the fact that local variables are “re-created” every time a function is called, brings up an interesting question. What happens to local variables when the function call that created them is no longer active?
+The ability to treat functions as values, combined with the fact that local bindings are re-created every time a function is called, brings up an interesting question. What happens to local bindings when the function call that created them is no longer active?
-The following code shows an example of this. It defines a function, `wrapValue`, which creates a local variable. It then returns a function that accesses and returns this local variable.
+The following code shows an example of this. It defines a function, `wrapValue`, that creates a local binding. It then returns a function that accesses and returns this local binding.
```
function wrapValue(n) {
- var localVariable = n;
- return function() { return localVariable; };
+ let local = n;
+ return () => local;
}
-var wrap1 = wrapValue(1);
-var wrap2 = wrapValue(2);
+let wrap1 = wrapValue(1);
+let wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2
```
-This is allowed and works as you'd hope—the variable can still be accessed. In fact, multiple instances of the variable can be alive at the same time, which is another good illustration of the concept that local variables really are re-created for every call—different calls can't trample on one another's local variables.
+This is allowed and works as you'd hope—both instances of the binding can still be accessed. This situation is a good demonstration of the fact that local bindings are created anew for every call, and different calls can't trample on one another's local bindings.
-This feature—being able to reference a specific instance of local variables in an enclosing function—is called _closure_. A function that “closes over” some local variables is called _a_ closure. This behavior not only frees you from having to worry about lifetimes of variables but also allows for some creative use of function values.
+This feature—being able to reference a specific instance of a local binding in an enclosing scope—is called _closure_. A function that references bindings from local scopes around it is called _a_ closure. This behavior not only frees you from having to worry about lifetimes of bindings but also makes it possible to use function values in some creative ways.
-With a slight change, we can turn the previous example into a way to create functions that multiply by an arbitrary amount.
+With a slight change, we can turn the previous example into a way to create functions that multiply by an arbitrary amount:
```
function multiplier(factor) {
- return function(number) {
- return number * factor;
- };
+ return number => number * factor;
}
-var twice = multiplier(2);
+let twice = multiplier(2);
console.log(twice(5));
// → 10
```
-The explicit `localVariable` from the `wrapValue` example isn't needed since a parameter is itself a local variable.
+The explicit `local` binding from the `wrapValue` example isn't really needed since a parameter is itself a local binding.
-Thinking about programs like this takes some practice. A good mental model is to think of the `function` keyword as “freezing” the code in its body and wrapping it into a package (the function value). So when you read `return function(...) {...}`, think of it as returning a handle to a piece of computation, frozen for later use.
+Thinking about programs like this takes some practice. A good mental model is to think of function values as containing both the code in their body and the environment in which they are created. When called, the function body sees the environment in which it was created, not the environment in which it is called.
-In the example, `multiplier` returns a frozen chunk of code that gets stored in the `twice` variable. The last line then calls the value in this variable, causing the frozen code (`return number * factor;`) to be activated. It still has access to the `factor` variable from the `multiplier` call that created it, and in addition it gets access to the argument passed when unfreezing it, 5, through its `number` parameter.
+In the example, `multiplier` is called and creates an environment in which its `factor` parameter is bound to 2\. The function value it returns, which is stored in `twice`, remembers this environment. So when that is called, it multiplies its argument by 2.
## Recursion
-It is perfectly okay for a function to call itself, as long as it takes care not to overflow the stack. A function that calls itself is called _recursive_. Recursion allows some functions to be written in a different style. Take, for example, this alternative implementation of `power`:
+It is perfectly okay for a function to call itself, as long as it doesn't do it so often that it overflows the stack. A function that calls itself is called _recursive_. Recursion allows some functions to be written in a different style. Take, for example, this alternative implementation of `power`:
```
function power(base, exponent) {
- if (exponent == 0)
+ if (exponent == 0) {
return 1;
- else
+ } else {
return base * power(base, exponent - 1);
+ }
}
console.log(power(2, 3));
// → 8
```
-This is rather close to the way mathematicians define exponentiation and arguably describes the concept in a more elegant way than the looping variant does. The function calls itself multiple times with different arguments to achieve the repeated multiplication.
+This is rather close to the way mathematicians define exponentiation and arguably describes the concept more clearly than the looping variant. The function calls itself multiple times with ever smaller exponents to achieve the repeated multiplication.
-But this implementation has one important problem: in typical JavaScript implementations, it's about 10 times slower than the looping version. Running through a simple loop is a lot cheaper than calling a function multiple times.
+But this implementation has one problem: in typical JavaScript implementations, it's about three times slower than the looping version. Running through a simple loop is generally cheaper than calling a function multiple times.
-The dilemma of speed versus elegance is an interesting one. You can see it as a kind of continuum between human-friendliness and machine-friendliness. Almost any program can be made faster by making it bigger and more convoluted. The programmer must decide on an appropriate balance.
+The dilemma of speed versus elegance is an interesting one. You can see it as a kind of continuum between human-friendliness and machine-friendliness. Almost any program can be made faster by making it bigger and more convoluted. The programmer has to decide on an appropriate balance.
-In the case of the [earlier](03_functions.html#power) `power` function, the inelegant (looping) version is still fairly simple and easy to read. It doesn't make much sense to replace it with the recursive version. Often, though, a program deals with such complex concepts that giving up some efficiency in order to make the program more straightforward becomes an attractive choice.
+In the case of the `power` function, the inelegant (looping) version is still fairly simple and easy to read. It doesn't make much sense to replace it with the recursive version. Often, though, a program deals with such complex concepts that giving up some efficiency in order to make the program more straightforward is helpful.
-The basic rule, which has been repeated by many programmers and with which I wholeheartedly agree, is to not worry about efficiency until you know for sure that the program is too slow. If it is, find out which parts are taking up the most time, and start exchanging elegance for efficiency in those parts.
+Worrying about efficiency can be a distraction. It's yet another factor that complicates program design, and when you're doing something that's already difficult, that extra thing to worry about can be paralyzing.
-Of course, this rule doesn't mean one should start ignoring performance altogether. In many cases, like the `power` function, not much simplicity is gained from the “elegant” approach. And sometimes an experienced programmer can see right away that a simple approach is never going to be fast enough.
+Therefore, always start by writing something that's correct and easy to understand. If you're worried that it's too slow—which it usually isn't, since most code simply isn't executed often enough to take any significant amount of time—you can measure afterwards and improve it if necessary.
-The reason I'm stressing this is that surprisingly many beginning programmers focus fanatically on efficiency, even in the smallest details. The result is bigger, more complicated, and often less correct programs, that take longer to write than their more straightforward equivalents and that usually run only marginally faster.
+Recursion is not always just an inefficient alternative to looping. Some problems really are easier to solve with recursion than with loops. Most often these are problems that require exploring or processing several “branches”, each of which might branch out again into even more branches.
-But recursion is not always just a less-efficient alternative to looping. Some problems are much easier to solve with recursion than with loops. Most often these are problems that require exploring or processing several “branches”, each of which might branch out again into more branches.
+Consider this puzzle: by starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produces that number?
-Consider this puzzle: by starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produce that number? For example, the number 13 could be reached by first multiplying by 3 and then adding 5 twice, whereas the number 15 cannot be reached at all.
+For example, the number 13 could be reached by first multiplying by 3 and then adding 5 twice, whereas the number 15 cannot be reached at all.
Here is a recursive solution:
```
function findSolution(target) {
function find(current, history) {
- if (current == target)
+ if (current == target) {
return history;
- else if (current > target)
+ } else if (current > target) {
return null;
- else
- return find(current + 5, "(" + history + " + 5)") ||
- find(current * 3, "(" + history + " * 3)");
+ } else {
+ return find(current + 5, `(${history} + 5)`) ||
+ find(current * 3, `(${history} * 3)`);
+ }
}
return find(1, "1");
}
@@ -360,11 +387,11 @@ console.log(findSolution(24));
Note that this program doesn't necessarily find the _shortest_ sequence of operations. It is satisfied when it finds any sequence at all.
-I don't necessarily expect you to see how it works right away. But let's work through it, since it makes for a great exercise in recursive thinking.
+It is okay if you don't see how it works right away. Let's work through it, since it makes for a great exercise in recursive thinking.
-The inner function `find` does the actual recursing. It takes two arguments—the current number and a string that records how we reached this number—and returns either a string that shows how to get to the target or `null`.
+The inner function `find` does the actual recursing. It takes two arguments: The current number and a string that records how we reached this number. If it finds a solution, it returns a string that shows how to get to the target. If no solution can be found starting from this number, it returns `null`.
-To do this, the function performs one of three actions. If the current number is the target number, the current history is a way to reach that target, so it is simply returned. If the current number is greater than the target, there's no sense in further exploring this history since both adding and multiplying will only make the number bigger. And finally, if we're still below the target, the function tries both possible paths that start from the current number, by calling itself twice, once for each of the allowed next steps. If the first call returns something that is not `null`, it is returned. Otherwise, the second call is returned—regardless of whether it produces a string or `null`.
+To do this, the function performs one of three actions. If the current number is the target number, the current history is a way to reach that target, so it is returned. If the current number is greater than the target, there's no sense in further exploring this branch because both adding and multiplying will only make the number bigger, so it returns `null`. And finally, if we're still below the target number, the function tries both possible paths that start from the current number by calling itself twice, once for addition and once for multiplication. If the first call returns something that is not `null`, it is returned. Otherwise, the second call is returned, regardless of whether it produces a string or `null`.
To better understand how this function produces the effect we're looking for, let's look at all the calls to `find` that are made when searching for a solution for the number 13.
@@ -384,15 +411,15 @@ find(1, "1")
found!
```
-The indentation suggests the depth of the call stack. The first time `find` is called it calls itself twice to explore the solutions that start with `(1 + 5)` and `(1 * 3)`. The first call tries to find a solution that starts with `(1 + 5)` and, using recursion, explores _every_ solution that yields a number less than or equal to the target number. Since it doesn't find a solution that hits the target, it returns `null` back to the first call. There the `||` operator causes the call that explores `(1 * 3)` to happen. This search has more luck because its first recursive call, through yet _another_ recursive call, hits upon the target number, 13\. This innermost recursive call returns a string, and each of the `||` operators in the intermediate calls pass that string along, ultimately returning our solution.
+The indentation indicates the depth of the call stack. The first time `find` is called, it starts by calling itself to explore the solution that starts with `(1 + 5)`. That call will further recurse to explore _every_ continued solution that yields a number less than or equal to the target number. Since it doesn't find one that hits the target, it returns `null` back to the first call. There the `||` operator causes the call that explores `(1 * 3)` to happen. This search has more luck—its first recursive call, through yet _another_ recursive call, hits upon the target number. That innermost call returns a string, and each of the `||` operators in the intermediate calls passes that string along, ultimately returning the solution.
## Growing functions
There are two more or less natural ways for functions to be introduced into programs.
-The first is that you find yourself writing very similar code multiple times. We want to avoid doing that since having more code means more space for mistakes to hide and more material to read for people trying to understand the program. So we take the repeated functionality, find a good name for it, and put it into a function.
+The first is that you find yourself writing very similar code multiple times. We'd prefer not to do that. Having more code means more space for mistakes to hide and more material to read for people trying to understand the program. So we take the repeated functionality, find a good name for it, and put it into a function.
-The second way is that you find you need some functionality that you haven't written yet and that sounds like it deserves its own function. You'll start by naming the function, and you'll then write its body. You might even start writing code that uses the function before you actually define the function itself.
+The second way is that you find you need some functionality that you haven't written yet and that sounds like it deserves its own function. You'll start by naming the function, and then you'll write its body. You might even start writing code that uses the function before you actually define the function itself.
How difficult it is to find a good name for a function is a good indication of how clear a concept it is that you're trying to wrap. Let's go through an example.
@@ -403,34 +430,37 @@ We want to write a program that prints two numbers, the numbers of cows and chic
011 Chickens
```
-That clearly asks for a function of two arguments. Let's get coding.
+This asks for a function of two arguments—the number of cows and the number of chickens. Let's get coding.
```
function printFarmInventory(cows, chickens) {
- var cowString = String(cows);
- while (cowString.length < 3)
+ let cowString = String(cows);
+ while (cowString.length < 3) {
cowString = "0" + cowString;
- console.log(cowString + " Cows");
- var chickenString = String(chickens);
- while (chickenString.length < 3)
+ }
+ console.log(`${cowString} Cows`);
+ let chickenString = String(chickens);
+ while (chickenString.length < 3) {
chickenString = "0" + chickenString;
- console.log(chickenString + " Chickens");
+ }
+ console.log(`${chickenString} Chickens`);
}
printFarmInventory(7, 11);
```
-Adding `.length` after a string value will give us the length of that string. Thus, the `while` loops keep adding zeros in front of the number strings until they are at least three characters long.
+Writing `.length` after a string expression will give us the length of that string. Thus, the `while` loops keep adding zeros in front of the number strings until they are at least three characters long.
-Mission accomplished! But just as we are about to send the farmer the code (along with a hefty invoice, of course), he calls and tells us he's also started keeping pigs, and couldn't we please extend the software to also print pigs?
+Mission accomplished! But just as we are about to send the farmer the code (along with a hefty invoice), she calls and tells us she's also started keeping pigs, and couldn't we please extend the software to also print pigs?
We sure can. But just as we're in the process of copying and pasting those four lines one more time, we stop and reconsider. There has to be a better way. Here's a first attempt:
```
function printZeroPaddedWithLabel(number, label) {
- var numberString = String(number);
- while (numberString.length < 3)
+ let numberString = String(number);
+ while (numberString.length < 3) {
numberString = "0" + numberString;
- console.log(numberString + " " + label);
+ }
+ console.log(`${numberString} ${label}`);
}
function printFarmInventory(cows, chickens, pigs) {
@@ -448,44 +478,45 @@ Instead of lifting out the repeated part of our program wholesale, let's try to
```
function zeroPad(number, width) {
- var string = String(number);
- while (string.length < width)
+ let string = String(number);
+ while (string.length < width) {
string = "0" + string;
+ }
return string;
}
function printFarmInventory(cows, chickens, pigs) {
- console.log(zeroPad(cows, 3) + " Cows");
- console.log(zeroPad(chickens, 3) + " Chickens");
- console.log(zeroPad(pigs, 3) + " Pigs");
+ console.log(`${zeroPad(cows, 3)} Cows`);
+ console.log(`${zeroPad(chickens, 3)} Chickens`);
+ console.log(`${zeroPad(pigs, 3)} Pigs`);
}
printFarmInventory(7, 16, 3);
```
-A function with a nice, obvious name like `zeroPad` makes it easier for someone who reads the code to figure out what it does. And it is useful in more situations than just this specific program. For example, you could use it to help print nicely aligned tables of numbers.
+A function with a nice, obvious name like `zeroPad` makes it easier for someone who reads the code to figure out what it does. And such a function is useful in more situations than just this specific program. For example, you could use it to help print nicely aligned tables of numbers.
-How smart and versatile should our function be? We could write anything from a terribly simple function that simply pads a number so that it's three characters wide to a complicated generalized number-formatting system that handles fractional numbers, negative numbers, alignment of dots, padding with different characters, and so on.
+How smart and versatile _should_ our function be? We could write anything, from a terribly simple function that can only pad a number to be three characters wide, to a complicated generalized number-formatting system that handles fractional numbers, negative numbers, alignment of decimal dots, padding with different characters, and so on.
-A useful principle is not to add cleverness unless you are absolutely sure you're going to need it. It can be tempting to write general “frameworks” for every little bit of functionality you come across. Resist that urge. You won't get any real work done, and you'll end up writing a lot of code that no one will ever use.
+A useful principle is to not add cleverness unless you are absolutely sure you're going to need it. It can be tempting to write general “frameworks” for every bit of functionality you come across. Resist that urge. You won't get any real work done—you'll just be writing code that you never use.
## Functions and side effects
-Functions can be roughly divided into those that are called for their side effects and those that are called for their return value. (Though it is definitely also possible to have both side effects and return a value.)
+Functions can be roughly divided into those that are called for their side effects and those that are called for their return value. (Though it is definitely also possible to both have side effects and return a value.)
The first helper function in the farm example, `printZeroPaddedWithLabel`, is called for its side effect: it prints a line. The second version, `zeroPad`, is called for its return value. It is no coincidence that the second is useful in more situations than the first. Functions that create values are easier to combine in new ways than functions that directly perform side effects.
-A _pure_ function is a specific kind of value-producing function that not only has no side effects but also doesn't rely on side effects from other code—for example, it doesn't read global variables that are occasionally changed by other code. A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesn't do anything else). This makes it easy to reason about. A call to such a function can be mentally substituted by its result, without changing the meaning of the code. When you are not sure that a pure function is working correctly, you can test it by simply calling it, and know that if it works in that context, it will work in any context. Nonpure functions might return different values based on all kinds of factors and have side effects that might be hard to test and think about.
+A _pure_ function is a specific kind of value-producing function that not only has no side effects but also doesn't rely on side effects from other code—for example, it doesn't read global bindings whose value might change. A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesn't do anything else). A call to such a function can be substituted by its return value without changing the meaning of the code. When you are not sure that a pure function is working correctly, you can test it by simply calling it, and know that if it works in that context, it will work in any context. Nonpure functions tend to require more scaffolding to test.
-Still, there's no need to feel bad when writing functions that are not pure or to wage a holy war to purge them from your code. Side effects are often useful. There'd be no way to write a pure version of `console.log`, for example, and `console.log` is certainly useful. Some operations are also easier to express in an efficient way when we use side effects, so computing speed can be a reason to avoid purity.
+Still, there's no need to feel bad when writing functions that are not pure or to wage a holy war to purge them from your code. Side effects are often useful. There'd be no way to write a pure version of `console.log`, for example, and `console.log` is good to have. Some operations are also easier to express in an efficient way when we use side effects, so computing speed can be a reason to avoid purity.
## Summary
-This chapter taught you how to write your own functions. The `function` keyword, when used as an expression, can create a function value. When used as a statement, it can be used to declare a variable and give it a function as its value.
+This chapter taught you how to write your own functions. The `function` keyword, when used as an expression, can create a function value. When used as a statement, it can be used to declare a binding and give it a function as its value. Arrow functions are yet another way to create functions.
```
-// Create a function value f
-var f = function(a) {
+// Define f to hold a function value
+const f = function(a) {
console.log(a + 2);
};
@@ -493,17 +524,20 @@ var f = function(a) {
function g(a, b) {
return a * b * 3.5;
}
+
+// A less verbose function value
+let h = a => a % 3;
```
-A key aspect in understanding functions is understanding local scopes. Parameters and variables declared inside a function are local to the function, re-created every time the function is called, and not visible from the outside. Functions declared inside another function have access to the outer function's local scope.
+A key aspect in understanding functions is understanding scopes. Each block creates a new scope. Parameters and bindings declared in a given scope are local, and not visible from the outside. Bindings declared with `var` behave differently—they end up in the nearest function scope or the global scope.
-Separating the tasks your program performs into different functions is helpful. You won't have to repeat yourself as much, and functions can make a program more readable by grouping code into conceptual chunks, in the same way that chapters and sections help organize regular text.
+Separating the tasks your program performs into different functions is helpful. You won't have to repeat yourself as much, and functions can help organize a program by grouping code into pieces that do specific things.
## Exercises
### Minimum
-The [previous chapter](02_program_structure.html#return_values) introduced the standard function `Math.min` that returns its smallest argument. We can do that ourselves now. Write a function `min` that takes two arguments and returns their minimum.
+The [previous chapter](02_program_structure.html#return_values) introduced the standard function `Math.min` that returns its smallest argument. We can build something like that now. Write a function `min` that takes two arguments and returns their minimum.
```
// Your code here.
@@ -520,7 +554,7 @@ A function may contain multiple `return` statements.
### Recursion
-We've seen that `%` (the remainder operator) can be used to test whether a number is even or odd by using `% 2` to check whether it's divisible by two. Here's another way to define whether a positive whole number is even or odd:
+We've seen that `%` (the remainder operator) can be used to test whether a number is even or odd by using `% 2` to see whether it's divisible by two. Here's another way to define whether a positive whole number is even or odd:
* Zero is even.
@@ -528,9 +562,9 @@ We've seen that `%` (the remainder operator) can be used to test whether a numbe
* For any other number _N_, its evenness is the same as _N_ - 2.
-Define a recursive function `isEven` corresponding to this description. The function should accept a `number` parameter and return a Boolean.
+Define a recursive function `isEven` corresponding to this description. The function should accept a single parameter (a positive, whole number) and return a Boolean.
-Test it on 50 and 75\. See how it behaves on -1. Why? Can you think of a way to fix this?
+Test it on 50 and 75\. See how it behaves on -1\. Why? Can you think of a way to fix this?
```
// Your code here.
@@ -549,9 +583,9 @@ When given a negative number, the function will recurse again and again, passing
### Bean counting
-You can get the Nth character, or letter, from a string by writing `"string".charAt(N)`, similar to how you get its length with `"s".length`. The returned value will be a string containing only one character (for example, `"b"`). The first character has position zero, which causes the last one to be found at position `string.length - 1`. In other words, a two-character string has length 2, and its characters have positions 0 and 1.
+You can get the Nth character, or letter, from a string by writing `"string"[N]`. The returned value will be a string containing only one character (for example, `"b"`). The first character has position zero, which causes the last one to be found at position `string.&lt;wbr&gt;length - 1`. In other words, a two-character string has length 2, and its characters have positions 0 and 1.
-Write a function `countBs` that takes a string as its only argument and returns a number that indicates how many uppercase “B” characters are in the string.
+Write a function `countBs` that takes a string as its only argument and returns a number that indicates how many uppercase “B” characters there are in the string.
Next, write a function called `countChar` that behaves like `countBs`, except it takes a second argument that indicates the character that is to be counted (rather than counting only uppercase “B” characters). Rewrite `countBs` to make use of this new function.
@@ -564,6 +598,6 @@ console.log(countChar("kakkerlak", "k"));
// → 4
```
-A loop in your function will have to look at every character in the string by running an index from zero to one below its length (`&lt; string.length`). If the character at the current position is the same as the one the function is looking for, it adds 1 to a counter variable. Once the loop has finished, the counter can be returned.
+Your function will need a loop that looks at every character in the string. It can run an index from zero to one below its length (`&lt; string.&lt;wbr&gt;length`). If the character at the current position is the same as the one the function is looking for, it adds 1 to a counter variable. Once the loop has finished, the counter can be returned.
-Take care to make all the variables used in the function _local_ to the function by using the `var` keyword.
+Take care to make all the bindings used in the function _local_ to the function by properly declaring them with the `let` or `const` keyword.

1029
diff-en/2ech4-3ech4.diff Normal file

File diff suppressed because it is too large Load Diff

867
diff-en/2ech5-3ech5.diff Normal file
View File

@ -0,0 +1,867 @@
diff --git a/2ech5.md b/3ech5.md
index 7d19300..3a03670 100644
--- a/2ech5.md
+++ b/3ech5.md
@@ -1,6 +1,6 @@
# Chapter 5Higher-Order Functions
-> Tzu-li and Tzu-ssu were boasting about the size of their latest programs. Two-hundred thousand lines,' said Tzu-li, not counting comments!' Tzu-ssu responded, Pssh, mine is almost a **million** lines already.' Master Yuan-Ma said, My best program has five hundred lines.' Hearing this, Tzu-li and Tzu-ssu were enlightened.
+> Tzu-li and Tzu-ssu were boasting about the size of their latest programs. Two-hundred thousand lines,' said Tzu-li, not counting comments!' Tzu-ssu responded, Pssh, mine is almost a _million_ lines already.' Master Yuan-Ma said, My best program has five hundred lines.' Hearing this, Tzu-li and Tzu-ssu were enlightened.
>
> &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;
@@ -8,12 +8,12 @@
>
> &lt;footer&gt;C.A.R. Hoare, &lt;cite&gt;1980 ACM Turing Award Lecture&lt;/cite&gt;&lt;/footer&gt;
-A large program is a costly program, and not just because of the time it takes to build. Size almost always involves complexity, and complexity confuses programmers. Confused programmers, in turn, tend to introduce mistakes (_bugs_) into programs. A large program also provides a lot of space for these bugs to hide, making them hard to find.
+A large program is a costly program, and not just because of the time it takes to build. Size almost always involves complexity, and complexity confuses programmers. Confused programmers, in turn, introduce mistakes (_bugs_) into programs. A large program then provides a lot of space for these bugs to hide, making them hard to find.
-Let's briefly go back to the final two example programs in the introduction. The first is self-contained and six lines long.
+Let's briefly go back to the final two example programs in the introduction. The first is self-contained and six lines long:
```
-var total = 0, count = 1;
+let total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
@@ -21,7 +21,7 @@ while (count <= 10) {
console.log(total);
```
-The second relies on two external functions and is one line long.
+The second relies on two external functions and is one line long:
```
console.log(sum(range(1, 10)));
@@ -51,113 +51,71 @@ And the second recipe:
The second is shorter and easier to interpret. But you do need to understand a few more cooking-related words—_soak_, _simmer_, _chop_, and, I guess, _vegetable_.
-When programming, we can't rely on all the words we need to be waiting for us in the dictionary. Thus, you might fall into the pattern of the first recipe—work out the precise steps the computer has to perform, one by one, blind to the higher-level concepts that they express.
+When programming, we can't rely on all the words we need to be waiting for us in the dictionary. Thus you might fall into the pattern of the first recipe—work out the precise steps the computer has to perform, one by one, blind to the higher-level concepts that they express.
-It has to become second nature, for a programmer, to notice when a concept is begging to be abstracted into a new word.
+It is a useful skill, in programming, to notice when you are working at too low a level of abstraction.
-## Abstracting array traversal
+## Abstracting repetition
Plain functions, as we've seen them so far, are a good way to build abstractions. But sometimes they fall short.
-In the [previous chapter](04_data.html#data), this type of `for` loop made several appearances:
+It is common for a program to do something a given number of times. You can write a `for` loop for that, like this:
```
-var array = [1, 2, 3];
-for (var i = 0; i < array.length; i++) {
- var current = array[i];
- console.log(current);
+for (let i = 0; i < 10; i++) {
+ console.log(i);
}
```
-It's trying to say, “For each element in the array, log it to the console”. But it uses a roundabout way that involves a counter variable `i`, a check against the array's length, and an extra variable declaration to pick out the current element. Apart from being a bit of an eyesore, this provides a lot of space for potential mistakes. We might accidentally reuse the `i` variable, misspell `length` as `lenght`, confuse the `i` and `current` variables, and so on.
-
-So let's try to abstract this into a function. Can you think of a way?
-
-Well, it's easy to write a function that goes over an array and calls `console.log` on every element.
+Can we abstract “doing something _N_ times” as a function? Well, it's easy to write a function that calls `console.log` _N_ times.
```
-function logEach(array) {
- for (var i = 0; i < array.length; i++)
- console.log(array[i]);
+function repeatLog(n) {
+ for (let i = 0; i < n; i++) {
+ console.log(i);
+ }
}
```
-But what if we want to do something other than logging the elements? Since “doing something” can be represented as a function and functions are just values, we can pass our action as a function value.
+But what if we want to do something other than logging the numbers? Since “doing something” can be represented as a function and functions are just values, we can pass our action as a function value.
```
-function forEach(array, action) {
- for (var i = 0; i < array.length; i++)
- action(array[i]);
+function repeat(n, action) {
+ for (let i = 0; i < n; i++) {
+ action(i);
+ }
}
-forEach(["Wampeter", "Foma", "Granfalloon"], console.log);
-// → Wampeter
-// → Foma
-// → Granfalloon
+repeat(3, console.log);
+// → 0
+// → 1
+// → 2
```
-(In some browsers, calling `console.log` in this way does not work. You can use `alert` instead of `console.log` if this example fails to work.)
-
-Often, you don't pass a predefined function to `forEach` but create a function value on the spot instead.
+You don't have to pass a predefined function to `repeat`. Often, you'd want to create a function value on the spot instead.
```
-var numbers = [1, 2, 3, 4, 5], sum = 0;
-forEach(numbers, function(number) {
- sum += number;
+let labels = [];
+repeat(5, i => {
+ labels.push(`Unit ${i + 1}`);
});
-console.log(sum);
-// → 15
+console.log(labels);
+// → ["Unit 1", "Unit 2", "Unit 3", "Unit 4", "Unit 5"]
```
-This looks quite a lot like the classical `for` loop, with its body written as a block below it. However, now the body is inside the function value, as well as inside the parentheses of the call to `forEach`. This is why it has to be closed with the closing brace _and_ closing parenthesis.
-
-Using this pattern, we can specify a variable name for the current element (`number`), rather than having to pick it out of the array manually.
-
-In fact, we don't need to write `forEach` ourselves. It is available as a standard method on arrays. Since the array is already provided as the thing the method acts on, `forEach` takes only one required argument: the function to be executed for each element.
-
-To illustrate how helpful this is, let's look back at a function from [the previous chapter](04_data.html#analysis). It contains two array-traversing loops.
-
-```
-function gatherCorrelations(journal) {
- var phis = {};
- for (var entry = 0; entry < journal.length; entry++) {
- var events = journal[entry].events;
- for (var i = 0; i < events.length; i++) {
- var event = events[i];
- if (!(event in phis))
- phis[event] = phi(tableFor(event, journal));
- }
- }
- return phis;
-}
-```
-
-Working with `forEach` makes it slightly shorter and quite a bit cleaner.
-
-```
-function gatherCorrelations(journal) {
- var phis = {};
- journal.forEach(function(entry) {
- entry.events.forEach(function(event) {
- if (!(event in phis))
- phis[event] = phi(tableFor(event, journal));
- });
- });
- return phis;
-}
-```
+This is structured a little like a `for` loop—it first describes the kind of loop and then provides a body. However, the body is now written as a function value, which is wrapped in the parentheses of the call to `repeat`. This is why it has to be closed with the closing brace _and_ closing parenthesis. In cases like this example, where the body is a single small expression, you could also omit the curly braces and write the loop on a single line.
## Higher-order functions
-Functions that operate on other functions, either by taking them as arguments or by returning them, are called _higher-order functions_. If you have already accepted the fact that functions are regular values, there is nothing particularly remarkable about the fact that such functions exist. The term comes from mathematics, where the distinction between functions and other values is taken more seriously.
+Functions that operate on other functions, either by taking them as arguments or by returning them, are called _higher-order functions_. Since we have already seen that functions are regular values, there is nothing particularly remarkable about the fact that such functions exist. The term comes from mathematics, where the distinction between functions and other values is taken more seriously.
Higher-order functions allow us to abstract over _actions_, not just values. They come in several forms. For example, you can have functions that create new functions.
```
function greaterThan(n) {
- return function(m) { return m > n; };
+ return m => m > n;
}
-var greaterThan10 = greaterThan(10);
+let greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));
// → true
```
@@ -166,16 +124,16 @@ And you can have functions that change other functions.
```
function noisy(f) {
- return function(arg) {
- console.log("calling with", arg);
- var val = f(arg);
- console.log("called with", arg, "- got", val);
- return val;
+ return (...args) => {
+ console.log("calling with", args);
+ let result = f(...args);
+ console.log("called with", args, ", returned", result);
+ return result;
};
}
-noisy(Boolean)(0);
-// → calling with 0
-// → called with 0 - got false
+noisy(Math.min)(3, 2, 1);
+// → calling with [3, 2, 1]
+// → called with [3, 2, 1] , returned 1
```
You can even write functions that provide new types of control flow.
@@ -184,12 +142,9 @@ You can even write functions that provide new types of control flow.
function unless(test, then) {
if (!test) then();
}
-function repeat(times, body) {
- for (var i = 0; i < times; i++) body(i);
-}
-repeat(3, function(n) {
- unless(n % 2, function() {
+repeat(3, n => {
+ unless(n % 2 == 1, () => {
console.log(n, "is even");
});
});
@@ -197,431 +152,378 @@ repeat(3, function(n) {
// → 2 is even
```
-The lexical scoping rules that we discussed in [Chapter 3](03_functions.html#scoping) work to our advantage when using functions in this way. In the previous example, the `n` variable is a parameter to the outer function. Because the inner function lives inside the environment of the outer one, it can use `n`. The bodies of such inner functions can access the variables around them. They can play a role similar to the `{}` blocks used in regular loops and conditional statements. An important difference is that variables declared inside inner functions do not end up in the environment of the outer function. And that is usually a good thing.
-
-## Passing along arguments
-
-The `noisy` function defined earlier, which wraps its argument in another function, has a rather serious deficit.
+There is a built-in array method, `forEach` that provides something like a `for`/`of` loop as a higher-order function.
```
-function noisy(f) {
- return function(arg) {
- console.log("calling with", arg);
- var val = f(arg);
- console.log("called with", arg, "- got", val);
- return val;
- };
-}
+["A", "B"].forEach(l => console.log(l));
+// → A
+// → B
```
-If `f` takes more than one parameter, it gets only the first one. We could add a bunch of arguments to the inner function (`arg1`, `arg2`, and so on) and pass them all to `f`, but it is not clear how many would be enough. This solution would also deprive `f` of the information in `arguments.length`. Since we'd always pass the same number of arguments, it wouldn't know how many arguments were originally given.
+## Script data set
-For these kinds of situations, JavaScript functions have an `apply` method. You pass it an array (or array-like object) of arguments, and it will call the function with those arguments.
+One area where higher-order functions shine is data processing. In order to process data, we'll need some actual data. This chapter will use a data set about scripts—writing systems such as Latin, Cyrillic, or Arabic.
-```
-function transparentWrapping(f) {
- return function() {
- return f.apply(null, arguments);
- };
-}
-```
-
-That's a useless function, but it shows the pattern we are interested in—the function it returns passes all of the given arguments, and only those arguments, to `f`. It does this by passing its own `arguments` object to `apply`. The first argument to `apply`, for which we are passing `null` here, can be used to simulate a method call. We will come back to that in the [next chapter](06_object.html#call_method).
+Remember Unicode from [Chapter 1](01_values.html#unicode), the system that assigns a number to each character in written language. Most of these characters are associated with a specific script. The standard contains 140 different scripts—81 are still in use today, and 59 are historic.
-## JSON
+Though I can only fluently read Latin characters, I appreciate the fact that people are writing texts in at least 80 other writing systems, many of which I wouldn't even recognize. For example, here's a sample of Tamil handwriting.
-Higher-order functions that somehow apply a function to the elements of an array are widely used in JavaScript. The `forEach` method is the most primitive such function. There are a number of other variants available as methods on arrays. To familiarize ourselves with them, let's play around with another data set.
+<figure>![Tamil handwriting](img/tamil.png)</figure>
-A few years ago, someone crawled through a lot of archives and put together a book on the history of my family name (Haverbeke—meaning Oatbrook). I opened it hoping to find knights, pirates, and alchemists ... but the book turns out to be mostly full of Flemish farmers. For my amusement, I extracted the information on my direct ancestors and put it into a computer-readable format.
+The example data set contains some pieces of information about the 140 scripts defined in Unicode. It is available in the [coding sandbox](https://eloquentjavascript.net/code#5) for this chapter as the `SCRIPTS` binding. The binding contains an array of objects, each of which describes a script.
-The file I created looks something like this:
-
-```
-[
- {"name": "Emma de Milliano", "sex": "f",
- "born": 1876, "died": 1956,
- "father": "Petrus de Milliano",
- "mother": "Sophia van Damme"},
- {"name": "Carolus Haverbeke", "sex": "m",
- "born": 1832, "died": 1905,
- "father": "Carel Haverbeke",
- "mother": "Maria van Brussel"},
- … and so on
-]
```
-
-This format is called JSON (pronounced “Jason”), which stands for JavaScript Object Notation. It is widely used as a data storage and communication format on the Web.
-
-JSON is similar to JavaScript's way of writing arrays and objects, with a few restrictions. All property names have to be surrounded by double quotes, and only simple data expressions are allowed—no function calls, variables, or anything that involves actual computation. Comments are not allowed in JSON.
-
-JavaScript gives us functions, `JSON.stringify` and `JSON.parse`, that convert data to and from this format. The first takes a JavaScript value and returns a JSON-encoded string. The second takes such a string and converts it to the value it encodes.
-
-```
-var string = JSON.stringify({name: "X", born: 1980});
-console.log(string);
-// → {"name":"X","born":1980}
-console.log(JSON.parse(string).born);
-// → 1980
+{
+ name: "Coptic",
+ ranges: [[994, 1008], [11392, 11508], [11513, 11520]],
+ direction: "ltr",
+ year: -200,
+ living: false,
+ link: "https://en.wikipedia.org/wiki/Coptic_alphabet"
+}
```
-The variable `ANCESTRY_FILE`, available in the sandbox for this chapter and in [a downloadable file](http://eloquentjavascript.net/2nd_edition/code/ancestry.js) on the website, contains the content of my JSON file as a string. Let's decode it and see how many people it contains.
+Such an object tells you the name of the script, the Unicode ranges assigned to it, the direction in which it is written, the (approximate) origin time, whether it is still in use, and a link to more information. Direction may be `"ltr"` for left-to-right, `"rtl"` for right-to-left (the way Arabic and Hebrew text are written), or `"ttb"` for top-to-bottom (as with Mongolian writing).
-```
-var ancestry = JSON.parse(ANCESTRY_FILE);
-console.log(ancestry.length);
-// → 39
-```
+The `ranges` property contains an array of Unicode character ranges, each of which is a two-element array containing a lower and upper bound. Any character codes within these ranges are assigned to the script. The lower bound is inclusive (code 994 is a Coptic character), and the upper bound non-inclusive (code 1008 isn't).
-## Filtering an array
+## Filtering arrays
-To find the people in the ancestry data set who were young in 1924, the following function might be helpful. It filters out the elements in an array that don't pass a test.
+To find the scripts in the data set that are still in use, the following function might be helpful. It filters out the elements in an array that don't pass a test:
```
function filter(array, test) {
- var passed = [];
- for (var i = 0; i < array.length; i++) {
- if (test(array[i]))
- passed.push(array[i]);
+ let passed = [];
+ for (let element of array) {
+ if (test(element)) {
+ passed.push(element);
+ }
}
return passed;
}
-console.log(filter(ancestry, function(person) {
- return person.born > 1900 && person.born < 1925;
-}));
-// → [{name: "Philibert Haverbeke", …}, …]
+console.log(filter(SCRIPTS, script => script.living));
+// → [{name: "Adlam", …}, …]
```
-This uses the argument named `test`, a function value, to fill in a “gap” in the computation. The `test` function is called for each element, and its return value determines whether an element is included in the returned array.
-
-Three people in the file were alive and young in 1924: my grandfather, grandmother, and great-aunt.
+The function uses the argument named `test`, a function value, to fill a “gap” in the computation—the process of deciding which elements to collect.
Note how the `filter` function, rather than deleting elements from the existing array, builds up a new array with only the elements that pass the test. This function is _pure_. It does not modify the array it is given.
-Like `forEach`, `filter` is also a standard method on arrays. The example defined the function only in order to show what it does internally. From now on, we'll use it like this instead:
+Like `forEach`, `filter` is a standard array method. The example defined the function only in order to show what it does internally. From now on, we'll use it like this instead:
```
-console.log(ancestry.filter(function(person) {
- return person.father == "Carel Haverbeke";
-}));
-// → [{name: "Carolus Haverbeke", …}]
+console.log(SCRIPTS.filter(s => s.direction == "ttb"));
+// → [{name: "Mongolian", …}, …]
```
## Transforming with map
-Say we have an array of objects representing people, produced by filtering the `ancestry` array somehow. But we want an array of names, which is easier to read.
+Say we have an array of objects representing scripts, produced by filtering the `SCRIPTS` array somehow. But we want an array of names, which is easier to inspect.
-The `map` method transforms an array by applying a function to all of its elements and building a new array from the returned values. The new array will have the same length as the input array, but its content will have been “mapped” to a new form by the function.
+The `map` method transforms an array by applying a function to all of its elements and building a new array from the returned values. The new array will have the same length as the input array, but its content will have been _mapped_ to a new form by the function.
```
function map(array, transform) {
- var mapped = [];
- for (var i = 0; i < array.length; i++)
- mapped.push(transform(array[i]));
+ let mapped = [];
+ for (let element of array) {
+ mapped.push(transform(element));
+ }
return mapped;
}
-var overNinety = ancestry.filter(function(person) {
- return person.died - person.born > 90;
-});
-console.log(map(overNinety, function(person) {
- return person.name;
-}));
-// → ["Clara Aernoudts", "Emile Haverbeke",
-// "Maria Haverbeke"]
+let rtlScripts = SCRIPTS.filter(s => s.direction == "rtl");
+console.log(map(rtlScripts, s => s.name));
+// → ["Adlam", "Arabic", "Imperial Aramaic", …]
```
-Interestingly, the people who lived to at least 90 years of age are the same three people who we saw before—the people who were young in the 1920s, which happens to be the most recent generation in my data set. I guess medicine has come a long way.
-
-Like `forEach` and `filter`, `map` is also a standard method on arrays.
+Like `forEach` and `filter`, `map` is a standard array method.
## Summarizing with reduce
-Another common pattern of computation on arrays is computing a single value from them. Our recurring example, summing a collection of numbers, is an instance of this. Another example would be finding the person with the earliest year of birth in the data set.
+Another common thing to do with arrays is computing a single value from them. Our recurring example, summing a collection of numbers, is an instance of this. Another example would be finding the script with the most characters.
-The higher-order operation that represents this pattern is called _reduce_ (or sometimes _fold_). You can think of it as folding up the array, one element at a time. When summing numbers, you'd start with the number zero and, for each element, combine it with the current sum by adding the two.
+The higher-order operation that represents this pattern is called _reduce_ (sometimes also called _fold_). It builds a value by repeatedly taking a single element from the array and combining it with the current value. When summing numbers, you'd start with the number zero and, for each element, add that to the sum.
-The parameters to the `reduce` function are, apart from the array, a combining function and a start value. This function is a little less straightforward than `filter` and `map`, so pay close attention.
+The parameters to `reduce` are, apart from the array, a combining function and a start value. This function is a little less straightforward than `filter` and `map`, so look closely:
```
function reduce(array, combine, start) {
- var current = start;
- for (var i = 0; i < array.length; i++)
- current = combine(current, array[i]);
+ let current = start;
+ for (let element of array) {
+ current = combine(current, element);
+ }
return current;
}
-console.log(reduce([1, 2, 3, 4], function(a, b) {
- return a + b;
-}, 0));
+console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0));
// → 10
```
The standard array method `reduce`, which of course corresponds to this function, has an added convenience. If your array contains at least one element, you are allowed to leave off the `start` argument. The method will take the first element of the array as its start value and start reducing at the second element.
-To use `reduce` to find my most ancient known ancestor, we can write something like this:
+```
+console.log([1, 2, 3, 4].reduce((a, b) => a + b));
+// → 10
+```
+
+To use `reduce` (twice) to find the script with the most characters, we can write something like this:
```
-console.log(ancestry.reduce(function(min, cur) {
- if (cur.born < min.born) return cur;
- else return min;
+function characterCount(script) {
+ return script.ranges.reduce((count, [from, to]) => {
+ return count + (to - from);
+ }, 0);
+}
+
+console.log(SCRIPTS.reduce((a, b) => {
+ return characterCount(a) < characterCount(b) ? b : a;
}));
-// → {name: "Pauwels van Haverbeke", born: 1535, …}
+// → {name: "Han", …}
```
+The `characterCount` function reduces the ranges assigned to a script by summing their sizes. Note the use of destructuring in the parameter list of the reducer function. The second call to `reduce` then uses this to find the largest script by repeatedly comparing two scripts and returning the larger one.
+
+The Han script has over 89,000 characters assigned to it in the Unicode standard, making it by far the biggest writing system in the data set. Han is a script (sometimes) used for Chinese, Japanese, and Korean text. Those languages share a lot of characters, though they tend to write them differently. The (US-based) Unicode Consortium decided to treat them as a single writing system in order to save character codes. This is called _Han unification_ and still makes some people very angry.
+
## Composability
-Consider how we would have written the previous example (finding the person with the earliest year of birth) without higher-order functions. The code is not that much worse.
+Consider how we would have written the previous example (finding the biggest script) without higher-order functions. The code is not that much worse.
```
-var min = ancestry[0];
-for (var i = 1; i < ancestry.length; i++) {
- var cur = ancestry[i];
- if (cur.born < min.born)
- min = cur;
+let biggest = null;
+for (let script of SCRIPTS) {
+ if (biggest == null ||
+ characterCount(biggest) < characterCount(script)) {
+ biggest = script;
+ }
}
-console.log(min);
-// → {name: "Pauwels van Haverbeke", born: 1535, …}
+console.log(biggest);
+// → {name: "Han", …}
```
-There are a few more variables, and the program is two lines longer but still quite easy to understand.
+There are a few more bindings, and the program is four lines longer. But it is still very readable.
-Higher-order functions start to shine when you need to _compose_ functions. As an example, let's write code that finds the average age for men and for women in the data set.
+Higher-order functions start to shine when you need to _compose_ operations. As an example, let's write code that finds the average year of origin for living and dead scripts in the data set.
```
function average(array) {
- function plus(a, b) { return a + b; }
- return array.reduce(plus) / array.length;
+ return array.reduce((a, b) => a + b) / array.length;
}
-function age(p) { return p.died - p.born; }
-function male(p) { return p.sex == "m"; }
-function female(p) { return p.sex == "f"; }
-console.log(average(ancestry.filter(male).map(age)));
-// → 61.67
-console.log(average(ancestry.filter(female).map(age)));
-// → 54.56
+console.log(Math.round(average(
+ SCRIPTS.filter(s => s.living).map(s => s.year))));
+// → 1185
+console.log(Math.round(average(
+ SCRIPTS.filter(s => !s.living).map(s => s.year))));
+// → 209
```
-(It's a bit silly that we have to define `plus` as a function, but operators in JavaScript, unlike functions, are not values, so you can't pass them as arguments.)
-
-Instead of tangling the logic into a big loop, it is neatly composed into the concepts we are interested in—determining sex, computing age, and averaging numbers. We can apply these one by one to get the result we are looking for.
+So the dead scripts in Unicode are, on average, older than the living ones. This is not a terribly meaningful or surprising statistic. But I hope you'll agree that the code used to compute it isn't hard to read. You can see it as a pipeline: we start with all scripts, filter out the living (or dead) ones, take the years from those, average them, and round the result.
-This is _fabulous_ for writing clear code. Unfortunately, this clarity comes at a cost.
+You could definitely also write this computation as one big loop.
-## The cost
-
-In the happy land of elegant code and pretty rainbows, there lives a spoil-sport monster called _inefficiency_.
-
-A program that processes an array is most elegantly expressed as a sequence of cleanly separated steps that each do something with the array and produce a new array. But building up all those intermediate arrays is somewhat expensive.
-
-Likewise, passing a function to `forEach` and letting that method handle the array iteration for us is convenient and easy to read. But function calls in JavaScript are costly compared to simple loop bodies.
-
-And so it goes with a lot of techniques that help improve the clarity of a program. Abstractions add layers between the raw things the computer is doing and the concepts we are working with and thus cause the machine to perform more work. This is not an iron law—there are programming languages that have better support for building abstractions without adding inefficiencies, and even in JavaScript, an experienced programmer can find ways to write abstract code that is still fast. But it is a problem that comes up a lot.
+```
+let total = 0, count = 0;
+for (let script of SCRIPTS) {
+ if (script.living) {
+ total += script.year;
+ count += 1;
+ }
+}
+console.log(Math.round(total / count));
+// → 1185
+```
-Fortunately, most computers are insanely fast. If you are processing a modest set of data or doing something that has to happen only on a human time scale (say, every time the user clicks a button), then it _does not matter_ whether you wrote a pretty solution that takes half a millisecond or a super-optimized solution that takes a tenth of a millisecond.
+But it is harder to see what was being computed and how. And because intermediate results aren't represented as coherent values, it'd be a lot more work to extract something like `average` into a separate function.
-It is helpful to roughly keep track of how often a piece of your program is going to run. If you have a loop inside a loop (either directly or through the outer loop calling a function that ends up performing the inner loop), the code inside the inner loop will end up running _N_×_M_ times, where _N_ is the number of times the outer loop repeats and _M_ is the number of times the inner loop repeats within each iteration of the outer loop. If that inner loop contains another loop that makes _P_ rounds, its body will run _M_×_N_×_P_ times, and so on. This can add up to large numbers, and when a program is slow, the problem can often be traced to only a small part of the code, which sits inside an inner loop.
+In terms of what the computer is actually doing, these two approaches are also quite different. The first will build up new arrays when running `filter` and `map`, whereas the second only computes some numbers, doing less work. You can usually afford the readable approach, but if you're processing huge arrays, and doing so many times, the less abstract style might be worth the extra speed.
-## Great-great-great-great-...
+## Strings and character codes
-My grandfather, Philibert Haverbeke, is included in the data file. By starting with him, I can trace my lineage to find out whether the most ancient person in the data, Pauwels van Haverbeke, is my direct ancestor. And if he is, I would like to know how much DNA I theoretically share with him.
+One use of the data set would be figuring out what script a piece of text is using. Let's go through a program that does this.
-To be able to go from a parent's name to the actual object that represents this person, we first build up an object that associates names with people.
+Remember that each script has an array of character code ranges associated with it. So given a character code, we could use a function like this to find the corresponding script (if any):
```
-var byName = {};
-ancestry.forEach(function(person) {
- byName[person.name] = person;
-});
+function characterScript(code) {
+ for (let script of SCRIPTS) {
+ if (script.ranges.some(([from, to]) => {
+ return code >= from && code < to;
+ })) {
+ return script;
+ }
+ }
+ return null;
+}
-console.log(byName["Philibert Haverbeke"]);
-// → {name: "Philibert Haverbeke", …}
+console.log(characterScript(121));
+// → {name: "Latin", …}
```
-Now, the problem is not entirely as simple as following the `father` properties and counting how many we need to reach Pauwels. There are several cases in the family tree where people married their second cousins (tiny villages and all that). This causes the branches of the family tree to rejoin in a few places, which means I share more than 1/2&lt;sup&gt;_G_&lt;/sup&gt; of my genes with this person, where _G_ for the number of generations between Pauwels and me. This formula comes from the idea that each generation splits the gene pool in two.
+The `some` method is another higher-order function. It takes a test function and tells you if that function returns true for any of the elements in the array.
+
+But how do we get the character codes in a string?
-A reasonable way to think about this problem is to look at it as being analogous to `reduce`, which condenses an array to a single value by repeatedly combining values, left to right. In this case, we also want to condense our data structure to a single value but in a way that follows family lines. The _shape_ of the data is that of a family tree, rather than a flat list.
+In [Chapter 1](01_values.html) I mentioned that JavaScript strings are encoded as a sequence of 16-bit numbers. These are called _code units_. A Unicode character code was initially supposed to fit within such a unit (which gives you a little over 65,000 characters). When it became clear that wasn't going to be enough, many people balked at the need to use more memory per character. To address these concerns, UTF-16, the format used by JavaScript strings, was invented. It describes most common characters using a single 16-bit code unit, but uses a pair of two such units for others.
-The way we want to reduce this shape is by computing a value for a given person by combining values from their ancestors. This can be done recursively: if we are interested in person _A_, we have to compute the values for _A_'s parents, which in turn requires us to compute the value for _A_'s grandparents, and so on. In principle, that'd require us to look at an infinite number of people, but since our data set is finite, we have to stop somewhere. We'll allow a default value to be given to our reduction function, which will be used for people who are not in the data. In our case, that value is simply zero, on the assumption that people not in the list don't share DNA with the ancestor we are looking at.
+UTF-16 is generally considered a bad idea today. It seems almost intentionally designed to invite mistakes. It's easy to write programs that pretend code units and characters are the same thing. And if your language doesn't use two-unit characters, that will appear to work just fine. But as soon as someone tries to use such a program with some less common Chinese characters, it breaks. Fortunately, with the advent of emoji, everybody has started using two-unit characters, and the burden of dealing with such problems is more fairly distributed.
-Given a person, a function to combine values from the two parents of a given person, and a default value, `reduceAncestors` condenses a value from a family tree.
+Unfortunately, obvious operations on JavaScript strings, such as getting their length through the `length` property and accessing their content using square brackets, deal only with code units.
```
-function reduceAncestors(person, f, defaultValue) {
- function valueFor(person) {
- if (person == null)
- return defaultValue;
- else
- return f(person, valueFor(byName[person.mother]),
- valueFor(byName[person.father]));
- }
- return valueFor(person);
-}
+// Two emoji characters, horse and shoe
+let horseShoe = "🐴👟";
+console.log(horseShoe.length);
+// → 4
+console.log(horseShoe[0]);
+// → (Invalid half-character)
+console.log(horseShoe.charCodeAt(0));
+// → 55357 (Code of the half-character)
+console.log(horseShoe.codePointAt(0));
+// → 128052 (Actual code for horse emoji)
```
-The inner function (`valueFor`) handles a single person. Through the magic of recursion, it can simply call itself to handle the father and the mother of this person. The results, along with the person object itself, are passed to `f`, which returns the actual value for this person.
+JavaScript's `charCodeAt` method gives you a code unit, not a full character code. The `codePointAt` method, added later, does give a full Unicode character. So we could use that to get characters from a string. But the argument passed to `codePointAt` is still an index into the sequence of code units. So to run over all characters in a string, we'd still need to deal with the question of whether a character takes up one or two code units.
-We can then use this to compute the amount of DNA my grandfather shared with Pauwels van Haverbeke and divide that by four.
+In the [previous chapter](04_data.html#for_of_loop), I mentioned that a `for`/`of` loop can also be used on strings. Like `codePointAt`, this type of loop was introduced at a time where people were acutely aware of the problems with UTF-16\. When you use it to loop over a string, it gives you real characters, not code units.
```
-function sharedDNA(person, fromMother, fromFather) {
- if (person.name == "Pauwels van Haverbeke")
- return 1;
- else
- return (fromMother + fromFather) / 2;
+let roseDragon = "🌹🐉";
+for (let char of roseDragon) {
+ console.log(char);
}
-var ph = byName["Philibert Haverbeke"];
-console.log(reduceAncestors(ph, sharedDNA, 0) / 4);
-// → 0.00049
+// → 🌹
+// → 🐉
```
-The person with the name Pauwels van Haverbeke obviously shared 100 percent of his DNA with Pauwels van Haverbeke (there are no people who share names in the data set), so the function returns 1 for him. All other people share the average of the amounts that their parents share.
+If you have a character (which will be a string of one or two code units), you can use `codePointAt(0)` to get its code.
-So, statistically speaking, I share about 0.05 percent of my DNA with this 16th-century person. It should be noted that this is only a statistical approximation, not an exact amount. It is a rather small number, but given how much genetic material we carry (about 3 billion base pairs), there's still probably some aspect in the biological machine that is me that originates with Pauwels.
+## Recognizing text
-We could also have computed this number without relying on `reduceAncestors`. But separating the general approach (condensing a family tree) from the specific case (computing shared DNA) can improve the clarity of the code and allows us to reuse the abstract part of the program for other cases. For example, the following code finds the percentage of a person's known ancestors who lived past 70 (by lineage, so people may be counted multiple times):
+We have a `characterScript` function and a way to correctly loop over characters. The next step would be to count the characters that belong to each script. The following counting abstraction will be useful there:
```
-function countAncestors(person, test) {
- function combine(current, fromMother, fromFather) {
- var thisOneCounts = current != person && test(current);
- return fromMother + fromFather + (thisOneCounts ? 1 : 0);
+function countBy(items, groupName) {
+ let counts = [];
+ for (let item of items) {
+ let name = groupName(item);
+ let known = counts.findIndex(c => c.name == name);
+ if (known == -1) {
+ counts.push({name, count: 1});
+ } else {
+ counts[known].count++;
+ }
}
- return reduceAncestors(person, combine, 0);
-}
-function longLivingPercentage(person) {
- var all = countAncestors(person, function(person) {
- return true;
- });
- var longLiving = countAncestors(person, function(person) {
- return (person.died - person.born) >= 70;
- });
- return longLiving / all;
+ return counts;
}
-console.log(longLivingPercentage(byName["Emile Haverbeke"]));
-// → 0.129
-```
-Such numbers are not to be taken too seriously, given that our data set contains a rather arbitrary collection of people. But the code illustrates the fact that `reduceAncestors` gives us a useful piece of vocabulary for working with the family tree data structure.
+console.log(countBy([1, 2, 3, 4, 5], n => n > 2));
+// → [{name: false, count: 2}, {name: true, count: 3}]
+```
-## Binding
+The `countBy` function expects a collection (anything that we can loop over with `for`/`of`) and a function that computes a group name for a given element. It returns an array of objects, each of which names a group and tells you the amount of elements that were found in that group.
-The `bind` method, which all functions have, creates a new function that will call the original function but with some of the arguments already fixed.
+It uses another array method—`findIndex`. This method is somewhat like `indexOf`, but instead of looking for a specific value, it finds the first value for which the given function returns true. Like `indexOf`, it returns -1 when no such element is found.
-The following code shows an example of `bind` in use. It defines a function `isInSet` that tells us whether a person is in a given set of strings. To call `filter` in order to collect those person objects whose names are in a specific set, we can either write a function expression that makes a call to `isInSet` with our set as its first argument or _partially apply_ the `isInSet` function.
+Using `countBy`, we can write the function that tells us which scripts are used in a piece of text.
```
-var theSet = ["Carel Haverbeke", "Maria van Brussel",
- "Donald Duck"];
-function isInSet(set, person) {
- return set.indexOf(person.name) > -1;
+function textScripts(text) {
+ let scripts = countBy(text, char => {
+ let script = characterScript(char.codePointAt(0));
+ return script ? script.name : "none";
+ }).filter(({name}) => name != "none");
+
+ let total = scripts.reduce((n, {count}) => n + count, 0);
+ if (total == 0) return "No scripts found";
+
+ return scripts.map(({name, count}) => {
+ return `${Math.round(count * 100 / total)}% ${name}`;
+ }).join(", ");
}
-console.log(ancestry.filter(function(person) {
- return isInSet(theSet, person);
-}));
-// → [{name: "Maria van Brussel", …},
-// {name: "Carel Haverbeke", …}]
-console.log(ancestry.filter(isInSet.bind(null, theSet)));
-// → … same result
+console.log(textScripts('英国的狗说"woof", 俄罗斯的狗说"тяв"'));
+// → 61% Han, 22% Latin, 17% Cyrillic
```
-The call to `bind` returns a function that will call `isInSet` with `theSet` as first argument, followed by any remaining arguments given to the bound function.
+The function first counts the characters by name, using `characterScript` to assign them a name, and falling back to the string `"none"` for characters that aren't part of any script. The `filter` call drops the entry for `"none"` from the resulting array, since we aren't interested in those characters.
-The first argument, where the example passes `null`, is used for method calls, similar to the first argument to `apply`. I'll describe this in more detail in the [next chapter](06_object.html#call_method).
+To be able to compute percentages, we first need the total amount of characters that belong to a script, which we can compute with `reduce`. If no such characters are found, the function returns a specific string. Otherwise, it transforms the counting entries into readable strings with `map` and then combines them with `join`.
## Summary
-Being able to pass function values to other functions is not just a gimmick but a deeply useful aspect of JavaScript. It allows us to write computations with “gaps” in them as functions and have the code that calls these functions fill in those gaps by providing function values that describe the missing computations.
+Being able to pass function values to other functions is a deeply useful aspect of JavaScript. It allows us to write functions that model computations with “gaps” in them. The code that calls these functions can fill in the gaps by providing function values.
-Arrays provide a number of useful higher-order methods—`forEach` to do something with each element in an array, `filter` to build a new array with some elements filtered out, `map` to build a new array where each element has been put through a function, and `reduce` to combine all an array's elements into a single value.
-
-Functions have an `apply` method that can be used to call them with an array specifying their arguments. They also have a `bind` method, which is used to create a partially applied version of the function.
+Arrays provide a number of useful higher-order methods. You can use `forEach` to loop over the elements in an array. The `filter` method returns a new array containing only the elements that pass the predicate function. Transforming an array by putting each element through a function is done with `map`. You can use `reduce` to combine all the elements in an array into a single value. The `some` method tests whether any element matches a given predicate function. And `findIndex` finds the position of the first element that matches a predicate.
## Exercises
### Flattening
-Use the `reduce` method in combination with the `concat` method to “flatten” an array of arrays into a single array that has all the elements of the input arrays.
+Use the `reduce` method in combination with the `concat` method to “flatten” an array of arrays into a single array that has all the elements of the original arrays.
```
-var arrays = [[1, 2, 3], [4, 5], [6]];
+let arrays = [[1, 2, 3], [4, 5], [6]];
// Your code here.
// → [1, 2, 3, 4, 5, 6]
```
-### Mother-child age difference
+### Your own loop
-Using the example data set from this chapter, compute the average age difference between mothers and children (the age of the mother when the child is born). You can use the `average` function defined [earlier](05_higher_order.html#average_function) in this chapter.
+Write a higher-order function `loop` that provides something like a `for` loop statement. It takes a value, a test function, an update function, and a body function. Each iteration, it first runs the test function on the current loop value and stops if that returns false. Then it calls the body function, giving it the current value. And finally, it calls the update function to create a new value and starts from the beginning.
-Note that not all the mothers mentioned in the data are themselves present in the array. The `byName` object, which makes it easy to find a person's object from their name, might be useful here.
+When defining the function, you can use a regular loop to do the actual looping.
```
-function average(array) {
- function plus(a, b) { return a + b; }
- return array.reduce(plus) / array.length;
-}
-
-var byName = {};
-ancestry.forEach(function(person) {
- byName[person.name] = person;
-});
-
// Your code here.
-// → 31.2
+loop(3, n => n > 0, n => n - 1, console.log);
+// → 3
+// → 2
+// → 1
```
-Because not all elements in the `ancestry` array produce useful data (we can't compute the age difference unless we know the birth date of the mother), we will have to apply `filter` in some manner before calling `average`. You could do it as a first pass, by defining a `hasKnownMother` function and filtering on that first. Alternatively, you could start by calling `map` and in your mapping function return either the age difference or `null` if no mother is known. Then, you can call `filter` to remove the `null` elements before passing the array to `average`.
+### Everything
-### Historical life expectancy
+Analogous to the `some` method, arrays also have an `every` method. This one returns true when the given function returns true for _every_ element in the array. In a way, `some` is a version of the `||` operator that acts on arrays, and `every` is like the `&&` operator.
-When we looked up all the people in our data set that lived more than 90 years, only the latest generation in the data came out. Let's take a closer look at that phenomenon.
-
-Compute and output the average age of the people in the ancestry data set per century. A person is assigned to a century by taking their year of death, dividing it by 100, and rounding it up, as in `Math.ceil(person.died / 100)`.
+Implement `every` as a function that takes an array and a predicate function as parameters. Write two versions, one using a loop and one using the `some` method.
```
-function average(array) {
- function plus(a, b) { return a + b; }
- return array.reduce(plus) / array.length;
+function every(array, test) {
+ // Your code here.
}
-// Your code here.
-
-// → 16: 43.5
-// 17: 51.2
-// 18: 52.8
-// 19: 54.8
-// 20: 84.7
-// 21: 94
+console.log(every([1, 3, 5], n => n < 10));
+// → true
+console.log(every([2, 4, 16], n => n < 10));
+// → false
+console.log(every([], n => n < 10));
+// → true
```
-The essence of this example lies in grouping the elements of a collection by some aspect of theirs—splitting the array of ancestors into smaller arrays with the ancestors for each century.
+Like the `&&` operator, the `every` method can stop evaluating further elements as soon as it has found one that doesn't match. So the loop-based version can jump out of the loop—with `break` or `return`—as soon as it runs into an element for which the predicate function returns false. If the loop runs to its end without finding such an element, we know that all elements matched and we should return true.
-During the grouping process, keep an object that associates century names (numbers) with arrays of either person objects or ages. Since we do not know in advance what categories we will find, we'll have to create them on the fly. For each person, after computing their century, we test whether that century was already known. If not, add an array for it. Then add the person (or age) to the array for the proper century.
+To build `every` on top of `some`, we can apply _De Morgan's laws_, which state that `a && b` equals `!(!a || !b)`. This can be generalized to arrays, where all elements in the array match if there is no element in the array that does not match.
-Finally, a `for`/`in` loop can be used to print the average ages for the individual centuries.
+### Dominant writing direction
-For bonus points, write a function `groupBy` that abstracts the grouping operation. It should accept as arguments an array and a function that computes the group for an element in the array and returns an object that maps group names to arrays of group members.
+Write a function that computes the dominant writing direction in a string of text. Remember that each script object has a `direction` property that can be `"ltr"` (left-to-right), `"rtl"` (right-to-left), or `"ttb"` (top-to-bottom).
-### Every and then some
-
-Arrays also come with the standard methods `every` and `some`. Both take a predicate function that, when called with an array element as argument, returns true or false. Just like `&&` returns a true value only when the expressions on both sides are true, `every` returns true only when the predicate returns true for _all_ elements of the array. Similarly, `some` returns true as soon as the predicate returns true for _any_ of the elements. They do not process more elements than necessary—for example, if `some` finds that the predicate holds for the first element of the array, it will not look at the values after that.
-
-Write two functions, `every` and `some`, that behave like these methods, except that they take the array as their first argument rather than being a method.
+The dominant direction is the direction of a majority of the characters that have a script associated with them. The `characterScript` and `countBy` functions defined earlier in the chapter are probably useful here.
```
-// Your code here.
+function dominantDirection(text) {
+ // Your code here.
+}
-console.log(every([NaN, NaN, NaN], isNaN));
-// → true
-console.log(every([NaN, NaN, 4], isNaN));
-// → false
-console.log(some([NaN, 3, 4], isNaN));
-// → true
-console.log(some([2, 3, 4], isNaN));
-// → false
+console.log(dominantDirection("Hello!"));
+// → ltr
+console.log(dominantDirection("Hey, مساء الخير"));
+// → rtl
```
-The functions can follow a similar pattern to the [definition](05_higher_order.html#forEach) of `forEach` at the start of the chapter, except that they must return immediately (with the right value) when the predicate function returns false—or true. Don't forget to put another `return` statement after the loop so that the function also returns the correct value when it reaches the end of the array.
+Your solution might look a lot like the first half of the `textScripts` example. You again have to count characters by a criterion based on `characterScript`, and then filter out the part of the result that refers to uninteresting (script-less characters).
+
+Finding the direction with the highest character count can be done with `reduce`. If it's not clear how, refer back to the example earlier in the chapter, where `reduce` was used to find the script with the most characters.