1
0
mirror of https://github.com/apachecn/eloquent-js-3e-zh.git synced 2025-05-24 04:22:20 +00:00
This commit is contained in:
wizardforcel 2018-05-01 23:02:02 +08:00
parent 505443b4f5
commit 82df82c7c7

180
4.md
View File

@ -12,9 +12,9 @@
一方面,雅克非常高兴他没有变成经典的狼人。 与变成狼相比,变成松鼠的确会产生更少的问题。 他不必担心偶然吃掉邻居(那会很尴尬),而是担心被邻居的猫吃掉。 他在橡木树冠上的一个薄薄的树枝上醒来,赤身裸体并迷失方向。在这两次偶然之后,他在晚上锁上了房间的门窗,并在地板上放了几个核桃,来使自己忙起来。
这就解决了猫和树的问题。 但雅克宁愿完全摆脱他的状况。 不规律发生的变使他怀疑,它们可能会由某种东西触发。 有一段时间,他相信只有在他靠近橡树的日子里才会发生。 但是避开橡树不能阻止这个问题。
这就解决了猫和树的问题。 但雅克宁愿完全摆脱他的状况。 不规律发生的变使他怀疑,它们可能会由某种东西触发。 有一段时间,他相信只有在他靠近橡树的日子里才会发生。 但是避开橡树不能阻止这个问题。
雅克切换到了更科学的方法,开始每天记录他在某一天所做的每件事,以及他是否变形。 有了这些数据,他希望能够缩小触发变形的条件。
雅克切换到了更科学的方法,开始每天记录他在某一天所做的每件事,以及他是否变身。 有了这些数据,他希望能够缩小触发变身的条件。
他需要的第一个东西,是存储这些信息的数据结构。
@ -24,9 +24,9 @@
我们可以用字符串来创建 - 毕竟,字符串可以有任意长度,所以我们可以把大量数据放入它们中,并使用`"2 3 5 7 11"`作为我们的表示。 但这很笨拙。 你必须以某种方式提取数字,并将它们转换回数字才能访问它们。
幸运的是JavaScript提供了一种数据类型专门用于存储一系列的值。我们将这种数据类型称为数组array,将一连串的值写在方括号当中,值之间使用逗号()分隔。
幸运的是JavaScript提供了一种数据类型专门用于存储一系列的值。我们将这种数据类型称为数组将一连串的值写在方括号当中值之间使用逗号`,`)分隔。
```
```js
let listOfNumbers = [2, 3, 5, 7, 11];
console.log(listOfNumbers[2]);
// → 5
@ -44,9 +44,9 @@ console.log(listOfNumbers[2 - 1]);
在之前的章节中,我们已经看到了一些可疑的表达式,例如`myString.length`(获取字符串的长度)和`Math.max`(最大值函数)。 这些表达式可以访问某个值的属性。 在第一个中,我们访问`myString`中的`length`属性。 第二个中,我们访问`Math`对象(它是数学相关常量和函数的集合)中的名为`max`的属性。
在JavaScript中几乎所有的值都有属性。但null和undefined没有。如果你尝试访问null和undefined的属性会得到一个错误提示。
JavaScript 中,几乎所有的值都有属性。但`null``undefined`没有。如果你尝试访问`null``undefined`的属性,会得到一个错误提示。
```
```js
null.length;
// → TypeError: null has no properties
```
@ -63,7 +63,7 @@ null.length;
`length`属性之外,字符串和数组对象都包含一些持有函数值的属性。
```
```js
let doh = "Doh";
console.log(typeof doh.toUpperCase);
// → function
@ -73,13 +73,13 @@ console.log(doh.toUpperCase());
每个字符串都有`toUpperCase`属性。 调用时,它将返回所有字母转换为大写字符串的副本。 另外还有`toLowerCase`
有趣的是虽然我们没有在调用toUpperCase时传递任何参数但该函数访问了字符串“Doh”即被调用的属性所属的值。我们会在第6章中阐述这其中的原理。
有趣的是,虽然我们没有在调用`toUpperCase`时传递任何参数,但该函数访问了字符串`"Doh"`,即被调用的属性所属的值。我们会在第 6 章中阐述这其中的原理。
我们通常将包含函数的属性称为某个值的方法method。比如说“toUpperCase是字符串的一个方法”
我们通常将包含函数的属性称为某个值的方法。比如说,`toUpperCase`是字符串的一个方法
此示例演示了两种方法,可用于操作数组:
```
```js
let sequence = [1, 2, 3];
sequence.push(4);
sequence.push(5);
@ -101,7 +101,7 @@ console.log(sequence);
对象类型的值是任意的属性集合。 创建对象的一种方法是使用大括号作为表达式。
```
```js
let day1 = {
squirrel: false,
events: ["work", "touched tree", "pizza", "running"]
@ -117,7 +117,7 @@ console.log(day1.wolf);
大括号内有一列用逗号分隔的属性。 每个属性都有一个名字,后跟一个冒号和一个值。 当一个对象写为多行时,像这个例子那样,对它进行缩进有助于提高可读性。 名称不是有效绑定名称或有效数字的属性必须加引号。
```
```js
let descriptions = {
work: "Went to work",
"touched tree": "Touched a tree"
@ -136,7 +136,7 @@ let descriptions = {
`delete`运算符切断章鱼的触手。 这是一个一元运算符,当应用于对象属性时,将从对象中删除指定的属性。 这不是一件常见的事情,但它是可能的。
```
```js
let anObject = {left: 1, right: 2};
console.log(anObject.left);
// → 1
@ -153,14 +153,14 @@ console.log("right" in anObject);
为了找出对象具有的属性,可以使用`Object.keys`函数。 你给它一个对象,它返回一个字符串数组 - 对象的属性名称。
```
```js
console.log(Object.keys({x: 0, y: 0, z: 2}));
// → ["x", "y", "z"]
```
`Object.assign`函数可以将一个对象的所有属性复制到另一个对象中。
```
```js
let objectA = {a: 1, b: 2};
bject.assign(objectA, {b: 3, c: 4});
console.log(objectA);
@ -171,7 +171,7 @@ console.log(objectA);
我们将雅克的日记表示为对象数组。
```
```js
let journal = [
{events: ["work", "touched tree", "pizza",
"running", "television"],
@ -196,7 +196,7 @@ let journal = [
当我们有两个数字120 和 120 时,我们可以将它们看作完全相同的数字,不管它们是否指向相同的物理位。 使用对象时,拥有同一个对象的两个引用,和拥有包含相同属性的两个不同的对象,是有区别的。 考虑下面的代码:
```
```js
let object1 = {value: 10};
let object2 = object1;
let object3 = {value: 10};
@ -217,7 +217,7 @@ console.log(object3.value);
绑定可以是可变的或不变的,但这与它们的值的行为方式是分开的。 即使数值不变,您也可以使用`let`绑定来跟踪一个变化的数字,通过修改绑定所指向的值。与之类似,虽然对象的`const`绑定本身不可改变,并且始终指向相同对象,该对象的内容可能会改变。
```
```js
const score = {visitors: 0, home: 0};
// This is okay
score.visitors = 1;
@ -231,7 +231,7 @@ score = {visitors: 1, home: 1};
于是,雅克开始了他的 JavaScript 之旅,并搭建了用于保存每天记录的一套开发环境。
```
```js
let journal = [];
function addEntry(events, squirrel) {
@ -243,7 +243,7 @@ function addEntry(events, squirrel) {
那么,在每天晚上十点 -- 或者有时候是下一天的早晨,从它的书架顶部爬下来之后 -- 雅克记录了这一天。
```
```js
addEntry(["work", "touched tree", "pizza", "running",
"television"], false);
addEntry(["work", "ice cream", "cauliflower", "lasagna",
@ -257,13 +257,13 @@ addEntry(["weekend", "cycling", "break", "peanuts",
关联性是统计绑定之间的独立性的度量。 统计绑定与编程绑定不完全相同。 在统计学中,您通常会有一组度量,并且每个绑定都根据每个度量来测量。 绑定之间的相关性通常表示为从 -1 到 1 的值。 相关性为零意味着绑定不相关。 相关性为一表明两者完全相关 - 如果你知道一个,你也知道另一个。 负一意味着它们是完全相关的,但它们是相反的 - 当一个是真的时,另一个是假的。
为了计算两个布尔绑定之间的相关性度量,我们可以使用 phi 系数(`φ`)。 这是一个公式,输入为一个频率表格,包含观测绑定的不同组合的次数。 公式的输出是 -1 和 1 之间的数字。
为了计算两个布尔绑定之间的相关性度量,我们可以使用 phi 系数(`ϕ`)。 这是一个公式,输入为一个频率表格,包含观测绑定的不同组合的次数。 公式的输出是 -1 和 1 之间的数字。
我们可以将吃比萨的事件放在这样的频率表中,每个数字表示我们的度量中的组合的出现次数。
![](../Images/00126.jpeg)
如果我们将那个表格称为`n`,我们可以用下列公式自己算`φ`
如果我们将那个表格称为`n`,我们可以用下列公式自己算`ϕ`
![](../Images/00126.jpeg)
@ -271,9 +271,9 @@ addEntry(["weekend", "cycling", "break", "peanuts",
符号_n_lt; subgt; 01lt; / subgt;表明, 第一个绑定松鼠为假0第二个绑定披萨为真1。 在披萨表中_n_lt; subgt; 01lt; / subgt; 是 9。
_n_lt; subgt; 1lt; / subgt; 表示所有度量之和其中第一个绑定为true在示例表中为5。 同样_n_lt; subgt; 0lt; / subgt; 表示所有度量之和,其中第二个绑定为假。
`n1`表示所有度量之和,其中第一个绑定为`true`,在示例表中为 5。 同样,`n0`表示所有度量之和,其中第二个绑定为假。
因此我们以比萨表为例除法线上方的部分被除数为1×769×4=40而除法线下面的部分除数则是10×80×5×85的平方根也就是![](../Images/00127.jpeg)。计算结果`≈0.069,这个结果很小,因此吃比萨对是否变身成松鼠显然没有太大影响。
因此,我们以比萨表为例,除法线上方的部分(被除数)为`1×769×4=40`,而除法线下面的部分(除数)则是`10×80×5×85`的平方根,也就是 ![](../Images/00127.jpeg)。计算结果≈0.069`,这个结果很小,因此吃比萨对是否变身成松鼠显然没有太大影响。
### 计算关联性
@ -281,7 +281,7 @@ addEntry(["weekend", "cycling", "break", "peanuts",
下面这个函数用于计算数组的系数`ϕ`
```
```js
function phi(table) {
return (table[3] * table[0] - table[2] * table[1]) /
Math.sqrt((table[2] + table[3]) *
@ -294,13 +294,13 @@ console.log(phi([76, 9, 4, 1]));
// → 0.068599434
```
这将`φ`公式直接翻译成 JavaScript。 `Math.sqrt`是平方根函数,由标准 JavaScript 环境中的`Math`对象提供。 我们必须在表格中添加两个字段来获取字段,例如`n1`因为行和或者列和不直接存储在我们的数据结构中。
这将`ϕ`公式直接翻译成 JavaScript。 `Math.sqrt`是平方根函数,由标准 JavaScript 环境中的`Math`对象提供。 我们必须在表格中添加两个字段来获取字段,例如`n1`因为行和或者列和不直接存储在我们的数据结构中。
雅克花了三个月的时间记录日志。在本章的代码沙箱([http://eloquentjavascript.net/code/](http://eloquentjavascript.net/code/))的下载文件中,用`JOURNAL`绑定存储了该结果数据集合。
若要从这篇记录中提取出某个特定事件的 2 乘 2 表格,我们首先需要循环遍历整个记录,并计算出与变身成松鼠相关事件发生的次数。
```
```js
function hasEvent(event, entry) {
return entry.events.indexOf(event) != -1;
}
@ -330,7 +330,7 @@ console.log(tableFor("pizza", JOURNAL));
`tableFor`函数中,有一个这样的循环:
```
```js
for (let i = 0; i < JOURNAL.length; i++) {
let entry = JOURNAL[i];
// Do something with entry
@ -341,7 +341,7 @@ for (let i = 0; i < JOURNAL.length; i++) {
在现代 JavaScript 中有一个更简单的方法来编写这样的循环。
```
```js
for (let entry of JOURNAL) {
console.log(`${entry.events.length} events.`);
}
@ -353,7 +353,7 @@ for (let entry of JOURNAL) {
我们需要计算数据集中发生的每种类型事件的相关性。 为此,我们首先需要寻找每种类型的事件。
```
```js
function journalEvents(journal) {
let events = [];
for (let entry of journal) {
@ -374,7 +374,7 @@ console.log(journalEvents(JOURNAL));
使用它,我们可以看到所有的相关性。
```
```js
for (let event of journalEvents(JOURNAL)) {
console.log(event + ":", phi(tableFor(event, JOURNAL)));
}
@ -386,9 +386,9 @@ for (let event of journalEvents(JOURNAL)) {
// and so on...
```
绝大多数相关系数都趋近于0。显然摄入胡萝卜、面包或布丁并不会引发变身成松鼠。但是似乎在周末变身成松鼠的概率更高。让我们过滤结果,来仅仅显示大于 0.1 或小于 -0.1 的相关性。
绝大多数相关系数都趋近于 0。显然摄入胡萝卜、面包或布丁并不会导致变身成松鼠。但是似乎在周末变身成松鼠的概率更高。让我们过滤结果,来仅仅显示大于 0.1 或小于 -0.1 的相关性。
```
```js
for (let event of journalEvents(JOURNAL)) {
let correlation = phi(tableFor(event, JOURNAL));
if (correlation > 0.1 || correlation < -0.1) {
@ -408,7 +408,7 @@ for (let event of journalEvents(JOURNAL)) {
这太有意思了。让我们再仔细看看这些数据。
```
```js
for (let entry of JOURNAL) {
if (entry.events.includes("peanuts") &&
!entry.events.includes("brushed teeth")) {
@ -421,7 +421,7 @@ console.log(phi(tableFor("peanut teeth", JOURNAL)));
这是一个强有力的结果。 这种现象正好发生在雅克吃花生并且没有刷牙时。 如果他只是不注意口腔卫生,他从来没有注意到他的病痛。
知道这些之后,雅克完全停止吃花生,发现他的变消失了。
知道这些之后,雅克完全停止吃花生,发现他的变消失了。
几年来,雅克过得越来越好。 但是在某个时候他失去了工作。 因为他生活在一个糟糕的国家,没有工作就意味着没有医疗服务,所以他被迫在一个马戏团就业,在那里他扮演的是不可思议的松鼠人,在每场演出前都用花生酱塞满了它的嘴。
@ -429,9 +429,9 @@ console.log(phi(tableFor("peanut teeth", JOURNAL)));
在完成本章之前,我想向您介绍几个对象相关的概念。 我将首先介绍一些通常实用的数组方法。
我们在本章的前面已经了解了push和pop方法分别用于在数组末尾添加或删除元素。相应地在数组的开头添加或删除元素的方法分别是unshift和shift。
我们在本章的前面已经了解了`push``pop`方法,分别用于在数组末尾添加或删除元素。相应地,在数组的开头添加或删除元素的方法分别是`unshift``shift`
```
```js
let todoList = [];
function remember(task) {
todoList.push(task);
@ -448,31 +448,31 @@ function rememberUrgently(task) {
有一个与`indexOf`方法类似的方法,叫`lastIndexOf`,只不过`indexOf`从数组第一个元素向后搜索,而`lastIndexOf`从最后一个元素向前搜索。
```
```js
console.log([1, 2, 3, 2, 1].indexOf(2));
// → 1
console.log([1, 2, 3, 2, 1].lastIndexOf(2));
// → 3
```
indexOf和lastIndexOf方法都有一个可选参数可以用来指定搜索的起始位置。
`indexOf``lastIndexOf`方法都有一个可选参数,可以用来指定搜索的起始位置。
另一个基本方法是slice该方法接受一个起始索引和一个结束索引然后返回数组中两个索引范围内的元素。起始索引元素包含在返回结果中但结束索引元素不会包含在返回结果中。
另一个基本方法是`slice`,该方法接受一个起始索引和一个结束索引,然后返回数组中两个索引范围内的元素。起始索引元素包含在返回结果中,但结束索引元素不会包含在返回结果中。
```
```js
console.log([0, 1, 2, 3, 4].slice(2, 4));
// → [2, 3]
console.log([0, 1, 2, 3, 4].slice(2));
// → [2, 3, 4]
```
如果没有指定结束索引slice会返回从起始位置之后的所有元素。您也可以省略起始索引来复制整个数组。
如果没有指定结束索引,`slice`会返回从起始位置之后的所有元素。您也可以省略起始索引来复制整个数组。
`concat`方法可用于将数组粘在一起,来创建一个新数组,类似于`+`运算符对字符串所做的操作。
以下示例展示了`concat``slice`的作用。 它接受一个数组和一个索引,然后它返回一个新数组,该数组是原数组的副本,并且删除了给定索引处的元素:
```
```js
function remove(array, index) {
return array.slice(0, index)
.concat(array.slice(index + 1));
@ -485,9 +485,9 @@ console.log(remove(["a", "b", "c", "d", "e"], 2));
### 字符串及其属性
我们可以调用字符串的length或toUpperCase这样的属性但不能向字符串中添加任何新的属性。
我们可以调用字符串的`length``toUpperCase`这样的属性,但不能向字符串中添加任何新的属性。
```
```js
let kim = "Kim";
kim.age = 88;
console.log(kim.age);
@ -496,9 +496,9 @@ console.log(kim.age);
字符串、数字和布尔类型的值并不是对象,因此当你向这些值中添加属性时 JavaScript 并不会报错,但实际上你并没有将这些属性添加进去。前面说过,这些值是不变的,不能改变。
但这些类型包含一些内置属性。每个字符串中包含了若干方法供我们使用最有用的方法可能就是slice和indexOf了它们的功能与数组中的同名方法类似。
但这些类型包含一些内置属性。每个字符串中包含了若干方法供我们使用,最有用的方法可能就是`slice``indexOf`了,它们的功能与数组中的同名方法类似。
```
```js
console.log("coconuts".slice(4, 7));
// → nut
console.log("coconut".indexOf("u"));
@ -507,28 +507,28 @@ console.log("coconut".indexOf("u"));
一个区别是,字符串的`indexOf`可以搜索包含多个字符的字符串,而相应的数组方法仅查找单个元素。
```
```js
console.log("one two three".indexOf("ee"));
// → 11
```
trim方法用于删除字符串中开头和结尾的空白符号空格、换行符和制表符等符号
`trim`方法用于删除字符串中开头和结尾的空白符号(空格、换行符和制表符等符号)。
```
```js
console.log(" okay \n ".trim());
// → okay
```
上一章中的`zeroPad`函数也作为方法存在。 它被称为`padStart`,接受所需的长度和填充字符作为参数。
```
```js
console.log(String(6).padStart(3, "0"));
// → 006
```
你可以使用`split`,在另一个字符串的每个出现位置分割一个字符串,然后再用`join`把它连接在一起。
```
```js
let sentence = "Secretarybirds specialize in stomping";
let words = sentence.split(" ");
console.log(words);
@ -539,14 +539,14 @@ console.log(words.join(". "));
可以用`repeat`方法重复一个字符串,该方法创建一个新字符串,包含原始字符串的多个副本,并将其粘在一起。
```
```js
console.log("LA".repeat(3));
// → LALALA
```
我们已经看到了字符串类型的`length`属性。 访问字符串中的单个字符,看起来像访问数组元素(有一个警告,我们将在第 5 章中讨论)。
```
```js
let string = "abc";
console.log(string.length);
// → 3
@ -560,7 +560,7 @@ console.log(string[1]);
为了编写这样一个函数,你需要在函数的最后一个参数之前放三个点,如下所示:
```
```js
function max(...numbers) {
let result = -Infinity;
for (let number of numbers) {
@ -576,7 +576,7 @@ console.log(max(4, 1, 9, -2));
您可以使用类似的三点表示法,来使用参数数组调用函数。
```
```js
let numbers = [5, 1, 7];
console.log(max(...numbers));
// → 7
@ -586,7 +586,7 @@ console.log(max(...numbers));
方括号的数组表示法,同样允许三点运算符将另一个数组展开到新数组中:
```
```js
let words = ["never", "fully"];
console.log(["will", ...words, "understand"]);
// → ["will", "never", "fully", "understand"]
@ -594,17 +594,17 @@ console.log(["will", ...words, "understand"]);
### Math对象
正如我们所看到的那样Math对象中包含了许多与数字相关的工具函数比如Math.max求最大值、Math.min求最小值和Math.sqrt求平方根
正如我们所看到的那样,`Math`对象中包含了许多与数字相关的工具函数,比如`Math.max`(求最大值)、`Math.min`(求最小值)和`Math.sqrt`(求平方根)。
`Math`对象被用作一个容器来分组一堆相关的功能。 只有一个`Math`对象,它作为一个值几乎没有用处。 相反,它提供了一个命名空间,使所有这些函数和值不必是全局绑定。
过多的全局绑定会“污染”命名空间。全局绑定越多就越有可能一不小心把某些绑定的值覆盖掉。比如我们可能想在程序中使用名为max的绑定由于JavaScript将内置的max函数安全地放置在Math对象中因此不必担心max的值会被覆盖。
过多的全局绑定会“污染”命名空间。全局绑定越多,就越有可能一不小心把某些绑定的值覆盖掉。比如,我们可能想在程序中使用名为`max`的绑定,由于 JavaScript 将内置的`max`函数安全地放置在`Math`对象中,因此不必担心`max`的值会被覆盖。
当你去定义一个已经被使用的绑定名的时候,对于很多编程语言来说,都会阻止你这么做,至少会对这种行为发出警告。但是 JavaScript 不会,因此要小心这些陷阱。
让我们来继续了解Math对象。如果需要做三角运算Math对象可以帮助到你它包含cos余弦、sin正弦、tan正切和各自的反函数acos、asin和atan。Math.PI则表示数字πpi或至少是JavaScript中的数字近似值。在传统的程序设计当中,常量均以大写来标注。
让我们来继续了解`Math`对象。如果需要做三角运算,`Math`对象可以帮助到你,它包含`cos`(余弦)、`sin`(正弦)、`tan`(正切)和各自的反函数(`acos``asin``atan`)。`Math.PI`则表示数字`π`,或至少是 JavaScript 中的数字近似值。在传统的程序设计当中,常量均以大写来标注。
```
```js
function randomPointOnCircle(radius) {
let angle = Math.random() * 2 * Math.PI;
return {x: radius * Math.cos(angle),
@ -616,9 +616,9 @@ console.log(randomPointOnCircle(2));
如果你对正弦或余弦不大熟悉,不必担心。我们会在第 13 章用到它们时,再做进一步解释。
在上面的示例代码中使用了Math.random。每次调用该函数时会返回一个伪随机数范围在0包括~1不包括之间。
在上面的示例代码中使用了`Math.random`。每次调用该函数时,会返回一个伪随机数,范围在 0包括1不包括之间。
```
```js
console.log(Math.random());
// → 0.36993729369714856
console.log(Math.random());
@ -629,22 +629,22 @@ console.log(Math.random());
虽然计算机是确定性的机器,但如果给定相同的输入,它们总是以相同的方式作出反应 - 让它们产生随机显示的数字是可能的。 为此,机器会维护一些隐藏的值,并且每当您请求一个新的随机数时,它都会对该隐藏值执行复杂的计算来创建一个新值。 它存储一个新值并返回从中派生的一些数字。 这样,它可以以随机的方式产生新的,难以预测的数字。
如果我们想获取一个随机的整数而非小数可以使用Math.floor向下取整到与当前数字最接近的整数来处理Math.random的结果。
如果我们想获取一个随机的整数而非小数,可以使用`Math.floor`(向下取整到与当前数字最接近的整数)来处理`Math.random`的结果。
```
```js
console.log(Math.floor(Math.random() * 10));
// → 2
```
将随机数乘以10可以得到一个在0~10之间的数字。由于Math.floor是向下取整因此该函数会等概率地取到0~9中的任何一个数字。
将随机数乘以 10 可以得到一个在 0 到 10 之间的数字。由于`Math.floor`是向下取整,因此该函数会等概率地取到 0 到 9 中的任何一个数字。
还有两个函数分别是Math.ceil向上取整和Math.round四舍五入。以及`Math.abs`,它取数字的绝对值,这意味着它反转了负值,但保留了正值。
还有两个函数,分别是`Math.ceil`(向上取整)和`Math.round`(四舍五入)。以及`Math.abs`,它取数字的绝对值,这意味着它反转了负值,但保留了正值。
### 解构
让我们暂时回顾`phi`函数:
```
```js
function phi(table) {
return (table[3] * table[0] - table[2] * table[1]) /
Math.sqrt((table[2] + table[3]) *
@ -656,7 +656,7 @@ function phi(table) {
这个函数难以阅读的原因之一,是我们有一个指向数组的绑定,但我们更愿意拥有数组的元素的绑定,即`let n00 = table [0]`以及其他。 幸运的是,有一种简洁的方法可以在 JavaScript 中执行此操作。
+```
```js
function phi([n00, n01, n10, n11]) {
return (n11 * n00 - n10 * n01) /
Math.sqrt((n10 + n11) * (n00 + n01) *
@ -668,7 +668,7 @@ function phi([n00, n01, n10, n11]) {
类似的技巧适用于对象,使用大括号代替方括号。
```
```js
let {name} = {name: "Faraji", age: 23};
console.log(name);
// → Faraji
@ -688,7 +688,7 @@ JSON 看起来像 JavaScript 的数组和对象的表示方式,但有一些限
表示为 JSON 数据时,日记条目可能看起来像这样
```
```json
{
"squirrel": false,
"events": ["work", "touched tree", "pizza", "running"]
@ -697,7 +697,7 @@ JSON 看起来像 JavaScript 的数组和对象的表示方式,但有一些限
JavaScript 为我们提供了函数`JSON.stringify``JSON.parse`,来将数据转换为这种格式,以及从这种格式转换。 第一个函数接受 JavaScript 值并返回 JSON 编码的字符串。 第二个函数接受这样的字符串并将其转换为它编码的值。
```
```js
let string = JSON.stringify({squirrel: false,
events: ["weekend"]});
console.log(string);
@ -710,9 +710,9 @@ console.log(JSON.parse(string).events);
对象和数组(一种特殊对象)可以将几个值组合起来形成一个新的值。理论上说,我们可以将一组相关的元素打包成一个对象,并通过这个对象来访问这些元素,以避免管理那些支离破碎的元素。
在JavaScript中除了null和undefined以外绝大多数的值都含有属性。我们可以用value.prop或value["prop"]的方式来访问属性。对象使用名称来定义和存储一定数量的属性。另外数组中通常会包含不同数量的值并使用数字从0开始作为这些值的属性。
JavaScript 中,除了`null``undefined`以外,绝大多数的值都含有属性。我们可以用`value.prop``value["prop"]`来访问属性。对象使用名称来定义和存储一定数量的属性。另外,数组中通常会包含不同数量的值,并使用数字(从 0 开始)作为这些值的属性。
在数组中有一些具名属性比如length和一些方法。方法是作为属性存在的函数常常作用于其所属的值。
在数组中有一些具名属性,比如`length`和一些方法。方法是作为属性存在的函数,常常作用于其所属的值。
您可以使用特殊类型的`for`循环`for (let element of array)`来迭代数组。
@ -722,17 +722,17 @@ console.log(JSON.parse(string).events);
在本书的前言中,提到过一种很好的计算固定范围内数字之和的方法:
```
```js
console.log(sum(range(1, 10)));
```
编写一个range函数接受两个参数start和end然后返回包含start到end包括end之间的所有数字。
编写一个`range`函数,接受两个参数:`start``end`,然后返回包含`start``end`(包括`end`)之间的所有数字。
接着编写一个sum函数接受一个数字数组并返回所有数字之和。运行示例程序检查一下结果是不是55。
接着,编写一个`sum`函数,接受一个数字数组,并返回所有数字之和。运行示例程序,检查一下结果是不是 55。
附加题是修改range函数接受第3个可选参数指定构建数组时的步数step。如果没有指定步数构建数组时每步按1增长和旧函数行为一致。调用函数range1102应该返回[13579]。另外确保步数值为负数时也可以正常工作因此range52-1应该产生[5432]
附加题是修改`range`函数,接受第 3 个可选参数,指定构建数组时的步长(`step`)。如果没有指定步长,构建数组时,每步增长 1和旧函数行为一致。调用函数`range(1, 10, 2)`,应该返回`[1, 3, 5, 7, 9]`。另外确保步数值为负数时也可以正常工作,因此`range(5, 2, -1)`应该产生`[5, 4, 3, 2]`
```
```js
// Your code here.
console.log(range(1, 10));
@ -745,11 +745,11 @@ console.log(sum(range(1, 10)));
#### 逆转数组
数组有一个reverse方法它可以逆转数组中元素的次序。在本题中编写两个函数reverseArray和reverseArrayInPlace。第一个函数reverseArray接受一个数组作为参数返回一个新数组并逆转新数组中的元素次序。第二个函数reverseArrayInPlace与第一个函数的功能相同但是直接将数组作为参数进行修改来逆转数组中的元素次序。两者都不能使用标准的reverse方法。
数组有一个`reverse`方法,它可以逆转数组中元素的次序。在本题中,编写两个函数,`reverseArray``reverseArrayInPlace`。第一个函数`reverseArray`接受一个数组作为参数,返回一个新数组,并逆转新数组中的元素次序。第二个函数`reverseArrayInPlace`与第一个函数的功能相同,但是直接将数组作为参数进行修改来,逆转数组中的元素次序。两者都不能使用标准的`reverse`方法。
回想一下,在上一章中关于副作用和纯函数的讨论,哪个函数的写法的应用场景更广?哪个执行得更快?
```
```js
// Your code here.
console.log(reverseArray(["A", "B", "C"]));
@ -764,7 +764,7 @@ console.log(arrayValue);
对象作为一个值的容器它可以用来构建各种各样的数据结构。有一种通用的数据结构叫作列表list不要与数组混淆。列表是一种嵌套对象集合第一个对象拥有第二个对象的引用而第二个对象有第三个对象的引用依此类推。
```
```js
let list = {
value: 1,
rest: {
@ -781,13 +781,13 @@ let list = {
![](../Images/00158.jpeg)
使用列表的一个好处是,它们之间可以共享相同的子列表。举个例子,如果我们新建了两个值:{value0resultlist}和{value-1resultlist}list引用了我们前面定义的绑定。这是两个独立的列表但它们之间却共享了同一个数据结构该数据结构包含列表末尾的三个元素。而且我们前面定义的list仍然是包含三个元素的列表。
使用列表的一个好处是,它们之间可以共享相同的子列表。举个例子,如果我们新建了两个值:`{value: 0result: list}``{value: -1result: list`}`list`引用了我们前面定义的绑定)。这是两个独立的列表,但它们之间却共享了同一个数据结构,该数据结构包含列表末尾的三个元素。而且我们前面定义的`list`仍然是包含三个元素的列表。
编写一个函数arrayToList当给定参数[123]建立一个和示例相似的数据结构。然后编写一个listToArray函数将列表转换成数组。再编写一个工具函数prepend接受一个元素和一个列表然后创建一个新的列表将元素添加到输入列表的开头。最后编写一个函数nth接受一个列表和一个数并返回列表中指定位置的元素如果该元素不存在则返回undefined。
编写一个函数`arrayToList`,当给定参数`[1, 2, 3]`时,建立一个和示例相似的数据结构。然后编写一个`listToArray`函数,将列表转换成数组。再编写一个工具函数`prepend`,接受一个元素和一个列表,然后创建一个新的列表,将元素添加到输入列表的开头。最后编写一个函数`nth`,接受一个列表和一个数,并返回列表中指定位置的元素,如果该元素不存在则返回`undefined`
如果你觉得这都不是什么难题那么编写一个递归版本的nth函数。
如果你觉得这都不是什么难题,那么编写一个递归版本的`nth`函数。
```
```js
// Your code here.
console.log(arrayToList([10, 20]));
@ -802,15 +802,15 @@ console.log(nth(arrayToList([10, 20, 30]), 1));
#### 深层比较
==运算符可以判断对象是否相等。但有些时候,你希望比较的是对象中实际属性的值。
`==`运算符可以判断对象是否相等。但有些时候,你希望比较的是对象中实际属性的值。
编写一个函数deepEqual接受两个参数若两个对象是同一个值或两个对象中有相同属性且使用deepEqual比较属性值均返回true时返回true。
编写一个函数`deepEqual`,接受两个参数,若两个对象是同一个值或两个对象中有相同属性,且使用`deepEqual`比较属性值均返回`true`时,返回`true`
为了弄清楚通过身份(使用===运算符)还是其属性比较两个值,可以使用typeof运算符。如果对两个值使用typeof均返回“object”,则说明你应该进行深层比较。但需要考虑一个例外的情况:由于历史原因,typeof null也会返回“object”
为了弄清楚通过身份(使用`===`运算符)还是其属性比较两个值,可以使用`typeof`运算符。如果对两个值使用`typeof`均返回`"object"`,则说明你应该进行深层比较。但需要考虑一个例外的情况:由于历史原因,`typeof null`也会返回`"object"`
当您需要查看对象的属性来进行比较时,`Object.keys`函数将非常有用。
```
```js
// Your code here.
let obj = {here: {is: "an"}, object: 2};