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-29 18:29:10 +08:00
parent 827274fc54
commit deb946d9c1
2 changed files with 112 additions and 89 deletions

201
1.md
View File

@ -1,49 +1,59 @@
## 第1章 值、类型和运算符
## 一、值,类型和运算符
> Below the surface of the machine, the program moves. Without effort, it expands and contracts. In great harmony, electrons scatter and regroup. The forms on the monitor are but ripples on the water. The essence stays invisibly below.
> 原文:[Values, Types, and Operators](http://eloquentjavascript.net/01_values.html)
>
> Master Yuan-Ma, The Book of Programming
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
>
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
>
> 部分参考了[《JavaScript 编程精解(第 2 版)》](https://book.douban.com/subject/26707144/)
在计算机的世界当中只有数据。你可以读取、修改以及新建数据数据可以用来表示任何信息。所有的数据都用类似的方式进行存储长位bit序列。
> 在机器的表面之下,程序在运转。 它不费力就可以扩大和缩小。 在和谐的关系中,电子散开并重新聚合。 监视器上的表格只是水面上的涟漪。 本质隐藏在下面。
>
> Master Yuan-Ma《The Book of Programming》
我们通常使用二值0和1来表示位。在计算机中位可以用一个高电荷或者一个低电荷一个强信号或者一个弱信号抑或是光盘表面的凹凸点来表示。任何离散信息都可以简化成由0与1组成的序列因此这些信息都可以表示成位序列。
![](img/1-0.jpg)
举例来说用位序列来表示数字13。我们可以仿照写十进制数字的方法来写位序列只是在写位序列的时候用的不是十个不同的数字而是两个。其中数字的权值以2为因子从右到左依次递增。以下是用位序列来表示的数字13其中每一位数字下方标注了该位的权值
计算机世界里只有数据。 你可以读取数据,修改数据,创建新数据 - 但不能提及不是数据的东西。 所有这些数据都以位的长序列存储,因此基本相似。
位是任何类型的二值的东西,通常描述为零和一。 在计算机内部,他们有一些形式,例如高电荷或低电荷,强信号或弱信号,或 CD 表面上的亮斑点或暗斑点。 任何一段离散信息都可以简化为零和一的序列,从而以位表示。
例如,我们可以用位来表示数字 13。 它的原理与十进制数字相同,但不是 10 个不同的数字,而只有 2 个,每个数字的权重从右到左增加 2 倍。 以下是组成数字 13 的位,下方显示数字的权重:
```
0 0 0 0 1 1 0 1
128 64 32 16 8 4 2 1
```
因此,二进制数00001101或者说是8+4+1即等于13。
因此,这就是二进制数`00001101`,或者`8+4+1`,即 13。
### 1.1 
## 值
让我们设想一下包含大量位序列的情况在一台标准现代计算机的易失性数据存储设备中就会包含超过300亿位的数据。而非易失性存储设备硬盘之类的存储设备则可以存储更多的数据
想象一下位之海 - 一片它们的海洋。 典型的现代计算机的易失性数据存储器(工作存储器)中,有超过 300 亿位。非易失性存储(硬盘或等价物)往往还有几个数量级
为了能够顺利操作如此多的位数据而又不引起数据丢失我们可以将这些位划分成表示不同信息的块。在JavaScript中我们将这些数据块称为值。虽然所有的值都是由位序列构成的但是它们的功能却各不相同。每个值的类型决定了其功能定义。在JavaScript中包含6种基本的值类型数字number、字符串string、布尔值boolean、对象object、函数function和未定义类型undefined
为了能够在不丢失的情况下,处理这些数量的数据,我们必须将它们分成代表信息片段的块。 在 JavaScript 环境中,这些块称为值。 虽然所有值都是由位构成的,但他们起到不同的作用,每个值都有一个决定其作用的类型。 有些值是数字,有些值是文本片段,有些值是函数,等等
![The Ocean of Bits](img/bit-sea.png)
要创建一个值,你只需要调用它的名字。 这很方便。 你不必为你的值收集建筑材料或为其付费。 你只需要调用它,然后刷的一下,你就有了它。 当然,它们并不是真正凭空创造的。 每个值都必须存储在某个地方,如果你想同时使用大量的值,则可能会耗尽内存。 幸运的是,只有同时需要它们时,这才是一个问题。 只要你不再使用值,它就会消失,留下它的一部分作为下一代值的建筑材料。
创建值的时候,只需要调用其名称即可,非常方便。我们不需要预先了解新创建的值的类型或者为其预先开辟存储空间,只需调用某个值,即可立刻获取到它。当然,这些值也不会凭空产生,每个值总要有一个地方来进行存储,如果你想在同一时间存储海量的值数据,可能就会耗尽内存空间。不过幸运的是,只有你同时需要这么多数据的情况下,才会出现这种问题。如果你不再需要使用某个值了,这个值所对应的数据就会被清理和回收,供其他值来使用
本章将会介绍 JavaScript 程序当中的基本元素,包括简单的值类型以及值运算符
本章将会介绍JavaScript程序当中的基本元素包括简单的值类型以及值运算符。
## 数字
### 1.2 数字
数字number类型的值即数字值。在JavaScript中写成如下形式
数字(`Number`)类型的值即数字值。在 JavaScript 中写成如下形式:
```
13
```
在程序中使用这个值的时候就会将数字13以位序列的方式存放在计算机的内存当中。
在程序中使用这个值的时候,就会将数字 13 以位序列的方式存放在计算机的内存当中。
JavaScript使用固定长度为64的位序列来存储数字值。我们只能使用64位存储序列产生一定数量的组合因此可以表示的数字个数也是有限的。对于十进制数来说如果长度有N位那么我们可以用其表示10<sup class="calibre3">N</sup>个数字。同理对于二进制数来说如果长度有64位则可以表示2<sup class="calibre3">64</sup>个数字大约是1800亿亿18后面跟18个0个数字这个数量已经相当大了。
JavaScript使用固定数量的位64 位)来存储单个数字值。 你可以用 64 位创造很多模式,这意味着可以表示的不同数值是有限的。 对于`N`个十进制数字,可以表示的数值数量是`10^N`。 与之类似,给定 64 个二进制数字,你可以表示`2^64`个不同的数字,大约 18 亿亿18 后面有 18 个零)。太多了。
在过去计算机的内存容量要比现在小得多因此人们一般会使用长度为8或16的位序列来表示数字。在这么小的范围内数字很容易溢出即给定的位序列无法存储超过这个范围的数字。而现在即便是个人计算机也会配置大量内存因此我们使用64位数据块来存储数字也不是什么问题也就是说在处理超大数字的时候也无需担心溢出。
过去计算机内存很小,人们倾向于使用一组 8 位或 16 位来表示他们的数字。 这么小的数字很容易意外地溢出,最终得到的数字不能放在给定的位数中。 今天,即使是装在口袋里的电脑也有足够的内存,所以你可以自由使用 64 位的块,只有在处理真正的天文数字时才需要担心溢出。
但是在JavaScript中并不可以使用所有小于1800亿亿的数字。这块长度为64的位序列还要能够表示负数所以需要有一位符号位用来表示数字的正负。还有一个需要我们关注的问题是如何表示非整数为了实现该功能还需要使用一些位来存储小数点的位置。因此在JavaScript中实际可存储的数字范围是1900万亿9后面跟15个0这依旧是一个很大的数字
不过,并非所有 18 亿亿以下的整数都能放在 JavaScript 数值中。 这些位也存储负数,所以一位用于表示数字的符号。 一个更大的问题是,也必须表示非整数。 为此,一些位用于存储小数点的位置。 可以存储的实际最大整数更多地在 9 亿亿15 个零)的范围内 - 这仍然相当多
使用小数点来表示分数。
@ -51,62 +61,63 @@ JavaScript使用固定长度为64的位序列来存储数字值。我们只能
9.81
```
对于过大或过小的数字来说可以使用带e即exponent指数的科学技术法来表示并在e的后面紧跟该数的指数。
对于非常大或非常小的数字,你也可以通过输入`e`(表示指数),后面跟着指数来使用科学记数法:
```
2.998e8
```
2.998×10<sup class="calibre3">8</sup>=299800000
`2.998 * 10^8 = 299,800,000`
当计算小于前文当中提到的9000万亿的整数时其计算结果会十分精确不过在计算小数的时候精度却不高。正如pi无法使用有限个数的十进制数字表示一样在使用64位来存储分数时也同样会丢失一些精度。虽说如此但这类丢失精度只会在一些特殊情况下才会出现问题。因此我们需要注意在处理分数时将其视为近似值而非精确值。
当计算小于前文当中提到的 9000 万亿的整数时,其计算结果会十分精确,不过在计算小数的时候精度却不高。正如(`pi`)无法使用有限个数的十进制数字表示一样,在使用 64 位来存储分数时也同样会丢失一些精度。虽说如此,但这类丢失精度只会在一些特殊情况下才会出现问题。因此我们需要注意在处理分数时,将其视为近似值,而非精确值。
#### 1.2.1 算术
### 算术
与数字密切相关的就是算术。比如加法或者乘法之类的算术运算会使用两个数值并产生一个新的数字。JavaScript中的算术运算如下所示
与数字密切相关的就是算术。比如加法或者乘法之类的算术运算会使用两个数值并产生一个新的数字。JavaScript 中的算术运算如下所示:
```
100 + 4 * 11
```
我们把“+”和“*”符号称为运算符。第一个符号表示加法,第二个符号表示乘法。将一个运算符放在两个值之间,该运算符将会使用其旁边的两个值产生一个新值。
我们把`+``*`符号称为运算符。第一个符号表示加法,第二个符号表示乘法。将一个运算符放在两个值之间,该运算符将会使用其旁边的两个值产生一个新值。
在上述示例当中是“4加100再将加的结果乘以11”还是先做乘法再做加法如你所想这里会先做乘法。但在数学运算中我们可以将加法用括号括起来改变运算次序。
但是这个例子的意思是“将 4 和 100 相加,并将结果乘 11”还是是在加法之前计算乘法 正如你可能猜到的那样,乘法首先计算。 但是和数学一样,你可以通过将加法包在圆括号中来改变它:
```
(100 + 4) * 11
```
“–”运算符表示减法,“/”运算符则表示除法。
``运算符表示减法,`/`运算符则表示除法。
在运算符同时出现,并且没有括号的情况下,其运算顺序根据运算符优先级确定。示例中的乘法运算符优先级高于加法。而“/”运算符和“*”运算符优先级相同,“+”运算符和“–”运算符优先级也相同。当多个具有相同优先级的运算符相邻出现时,运算从左向右执行,比如12+1的运算顺序是12+1
在运算符同时出现,并且没有括号的情况下,其运算顺序根据运算符优先级确定。示例中的乘法运算符优先级高于加法。而`/`运算符和`*`运算符优先级相同,`+`运算符和``运算符优先级也相同。当多个具有相同优先级的运算符相邻出现时,运算从左向右执行,比如`12+1`的运算顺序是`(12)+1`
你无需担心这些运算符的优先级规则,不确定的时候只需要添加括号即可。
“%”符号表示余数运算读者可能对这个算术运算符不太熟悉。X%Y表示求X除以Y后所得的的余数。比如314%100的结果是14而144%12的结果是0。求余运算符的优先级和乘除法相同。该运算符常常被称为模数运算符但准确来说还是应该称其为求余运算符。
还有一个算术运算符,你可能无法立即认出。 `%`符号用于表示取余操作。 `X % Y``Y``X`的余数。 例如,`314 % 100`产生`14``144 % 12`产生`0`。 余数的优先级与乘法和除法的优先级相同。 你还经常会看到这个运算符被称为模运算符。
#### 1.2.2 特殊数字
### 特殊数字
在JavaScript中有三个特殊的值它们虽然是数字但看起来却跟一般的数字不太一样。
JavaScript 中有三个特殊的值,它们虽然是数字,但看起来却跟一般的数字不太一样。
前两个是Infinity和-Infinity它们分别表示正无穷大和负无穷大。Infinity1的结果仍然是Infinity以此类推。不要过于依赖无穷运算的结果因为这类运算并不真正属于数学运算由此我们可以引出下一个特殊数字NaN
前两个是`Infinity``-Infinity`,它们代表正无穷和负无穷。 “无穷减一”仍然是“无穷”,依此类推。 尽管如此,不要过分信任基于无穷大的计算。 它在数学上不合理,并且很快导致我们的下一个特殊数字:`NaN`
虽然NaN是数字类型的值但我们用其表示“非数值”。举例来说在计算0/00除以0、InfinityInfinity或当数值运算中产生不精确或毫无意义的结果时就用NaN来表示
`NaN`代表“不是数字”,即使它是数字类型的值。 例如,当你尝试计算`0/0`(零除零),`Infinity - Infinity`或任何其他数字操作,它不会产生有意义的结果时,你将得到此结果
### 1.3 字符串
## 字符串
另一个基本数据类型是字符串,我们使用字符串来表示文本信息。使用引号将内容括起来。
下一个基本数据类型是字符串(`String`)。 字符串用于表示文本。 它们是用引号括起来的:
```
"Patch my boat with chewing gum"
'Monkeys wave goodbye'
`Down on the sea`
"Lie on the ocean"
'Float on the ocean'
```
其中,单引号和双引号都可以用来标记字符串,只要保证字符串前后引号一致即可
只要字符串开头和结尾的引号匹配,就可以使用单引号,双引号或反引号来标记字符串
我们几乎可以在引号中填写任何字符JavaScript会使用这些字符来生成字符串。但填写有些字符会稍微复杂一些。比如将引号放在引号中就比较麻烦。另外由于字符串只能放在一行里所以换行符输入回车键所产生的字符也无法放在引号之间
几乎所有的东西都可以放在引号之间,并且 JavaScript 会从中提取字符串值。 但少数字符更难。 你可能难以想象,如何在引号之间加引号。 当使用反引号(`` ` ``)引用字符串时,换行符(当你按回车键时获得的字符)可能会被包含,而无需转义
若要将这些字符存入字符串,需要使用下列规则:当反斜杠(\出现在引号之间的文本中时表示紧跟在其后的字符具有特殊含义我们将其称之为转义符。当引号紧跟在反斜杠后时并不意味着字符串结束而表示这个引号是字符串的一部分。当字符n出现在反斜杠后时JavaScript将其解释成换行符。以此类推\t表示制表符我们来看看下面这个字符串
若要将这些字符存入字符串,需要使用下列规则:当反斜杠(`\`)出现在引号之间的文本中时,表示紧跟在其后的字符具有特殊含义,我们将其称之为转义符。当引号紧跟在反斜杠后时,并不意味着字符串结束,而表示这个引号是字符串的一部分。当字符`n`出现在反斜杠后时JavaScript 将其解释成换行符。以此类推,`\t`表示制表符,我们来看看下面这个字符串:
```
"This is the first line\nAnd this is the second"
@ -119,23 +130,35 @@ This is the first line
And this is the second
```
当然有时你希望字符串中的反斜杠就表示反斜杠本身而非特殊字符。你可以使用两个连续的反斜杠来表示一个反斜杠最后的字符串值中也只会留下一个反斜杠。因此字符串“A newline character is written like"\n"”可以写成
当然,在某些情况下,你希望字符串中的反斜杠只是反斜杠,而不是特殊代码。 如果两个反斜杠写在一起,它们将合并,并且只有一个将留在结果字符串值中。 这就是字符串“`A newline character is written like "\n".`”的表示方式
```
"A newline character is written like \"\\n\"."
```
我们不能将除法、乘法或减法运算符用于字符串,但是“+”运算符却可以。这种情况下运算符并不表示加法而是连接操作将两个字符串连接到一起。以下语句可以产生字符串“concatenate”
字符串也必须建模为一系列位,以便能够存在于计算机内部。 JavaScript 执行此操作的方式基于 Unicode 标准。 该标准为你几乎需要的每个字符分配一个数字,包括来自希腊语,阿拉伯语,日语,亚美尼亚语,以及其他的字符。 如果我们为每个字符分配一个数字,则可以用一系列数字来描述一个字符串。
这就是 JavaScript 所做的。 但是有一个复杂的问题JavaScript 的表示为每个字符串元素使用 16 位,它可以描述多达 2 的 16 次方个不同的字符。 但是Unicode 定义的字符多于此 - 大约是此处的两倍。 所以有些字符,比如许多 emoji在 JavaScript 字符串中占据了两个“字符位置”。 我们将在第 5 章中回来讨论。
我们不能将除法,乘法或减法运算符用于字符串,但是`+`运算符却可以。这种情况下,运算符并不表示加法,而是连接操作:将两个字符串连接到一起。以下语句可以产生字符串`"concatenate"`
```
"con" + "cat" + "e" + "nate"
```
还有很多其他方式来操作字符串我们会在第4章中进行讨论。
字符串值有许多相关的函数(方法),可用于对它们执行其他操作。 我们将在第 4 章中回来讨论。
### 1.4 一元运算符
用单引号或双引号编写的字符串的行为非常相似 - 唯一的区别是需要在其中转义哪种类型的引号。 反引号字符串,通常称为模板字面值,可以实现更多的技巧。 除了能够跨越行之外,它们还可以嵌入其他值。
并非所有的运算符都是用符号来表示还有一些运算符是用单词表示的。比如typeof运算符会产生一个字符串的值内容是给定值的具体类型。
```
`half of 100 is ${100 / 2}`
```
当你在模板字面值中的`$ {}`中写入内容时,将计算其结果,转换为字符串并包含在该位置。 这个例子产生`"half of 100 is 50"`
## 一元运算符
并非所有的运算符都是用符号来表示,还有一些运算符是用单词表示的。比如`typeof`运算符,会产生一个字符串的值,内容是给定值的具体类型。
```
console.log(typeof 4.5)
@ -144,20 +167,20 @@ console.log(typeof "x")
// → string
```
我们会在示例代码中使用console.log命令来打印并表示我们需要查看的一些运算结果。当你执行了这段代码后屏幕上就会显示出运算产生的值而如何显示结果则取决于执行程序所使用的JavaScript环境
我们将在示例代码中使用`console.log`,来表示我们希望看到求值结果。更多内容请见下一章
我们所见过的绝大多数运算符都使用两个值进行操作而typeof仅接受一个值进行操作。使用两个值的运算符称为二元运算符而使用一个值的则称为一元运算符。减号运算符既可用作一元运算符也可用作二元运算符。
我们所见过的绝大多数运算符都使用两个值进行操作,而`typeof`仅接受一个值进行操作。使用两个值的运算符称为二元运算符,而使用一个值的则称为一元运算符。减号运算符既可用作一元运算符,也可用作二元运算符。
```
console.log(- (10 - 2))
// → -8
```
### 1.5 布尔值
## 布尔值
我们常常需要用一个值来简单地区分两种可能性比如说“是”和“否”以及“开”和“关”。JavaScript使用布尔类型来表示这种情况该类型的值只有两种取值true和false就用这两个英文单词来表示
拥有一个值,它能区分两种可能性,通常是有用的,例如“是”和“否”或“开”和“关”。 为此JavaScript 拥有布尔(`Boolean`)类型,它有两个值:`true``false`,它们就写成这些单词
#### 1.5.1 比较
### 比较
一种产生布尔值的方法如下所示:
@ -168,7 +191,7 @@ console.log(3 < 2)
// → false
```
&gt;”和“&lt;符号分别表示“大于”和“小于”。这两个符号是二元运算符,通过该运算符返回的结果是一个布尔值,表示其运算是否为真。
`>``<`符号分别表示“大于”和“小于”。这两个符号是二元运算符,通过该运算符返回的结果是一个布尔值,表示其运算是否为真。
我们可以使用相同的方法比较字符串。
@ -177,29 +200,29 @@ console.log("Aardvark" < "Zoroaster")
// → true
```
字符串的比较是按照字母顺序来进行比较的,大写字母总是“小于”小写字母,因此"Z"&lt;"a"的结果为真另外字符顺序中也包括了非字母字符比如。实际上字符的比较是基于Unicode标准实现的。该标准为你需要的每个字符赋予了一个数字包括希腊文、阿拉伯文、日文和泰米尔文等。使用这些数字有助于将字符串存储在计算机中因为这样我们就可以将字符与数字一一对应并将字符串存储成数字的序列了。在比较字符串时JavaScript从左向右逐个比较每个字符对应的数字编码。
字符串排序的方式大致是字典序,但不真正是你期望从字典中看到的那样:大写字母总是比小写字母“小”,所以`"Z"<"A"`,非字母字符(`!``-`等)也包含在排序中。 比较字符串时JavaScript 从左向右遍历字符,逐个比较 Unicode 代码。
其他类似的运算符则包括&gt;=(大于等于)、&lt;=(小于等于)、==(等于)和!=(不等于)。
其他类似的运算符则包括`>=`(大于等于),`<=`(小于等于),`==`(等于)和`!=`(不等于)。
```
console.log("Itchy" != "Scratchy")
// → true
console.log("Apple" == "Orange")
// → false
```
在JavaScript中只有一个值不等于其自身那就是NaNNot a Number非数值
JavaScript 中,只有一个值不等于其自身,那就是`NaN`Not a Number非数值
```
console.log(NaN == NaN)
// → false
```
NaN用于表示非法运算的结果正因如此不同的非法运算结果也不会相等。
`NaN`用于表示非法运算的结果,正因如此,不同的非法运算结果也不会相等。
#### 1.5.2 逻辑运算符
### 逻辑运算符
还有一些运算符可以应用于布尔值上。JavaScript支持三种逻辑运算符andor和非not。这些运算符可以用于推理布尔值。
还有一些运算符可以应用于布尔值上。JavaScript 支持三种逻辑运算符andor和非not。这些运算符可以用于推理布尔值。
&&运算符表示逻辑与该运算符是二元运算符只有当赋给它的两个值均为true时其结果才是真。
`&&`运算符表示逻辑与,该运算符是二元运算符,只有当赋给它的两个值均为`true`时其结果才是真。
```
console.log(true && false)
@ -208,7 +231,7 @@ console.log(true && true)
// → true
```
||运算符表示逻辑或。当两个值中任意一个为true时结果就为真。
`||`运算符表示逻辑或。当两个值中任意一个为`true`时,结果就为真。
```
console.log(false || true)
@ -217,9 +240,9 @@ console.log(false || false)
// → false
```
感叹号(表示逻辑非该运算符是一元运算符用于反转给定的值比如true的结果是falsefalse结果是true
感叹号(`!`)表示逻辑非,该运算符是一元运算符,用于反转给定的值,比如`!true`的结果是`false`,而`!false`结果是`true`
在混合使用布尔运算符和其他运算符的情况下,总是很难确定什么时候需要使用括号。实际上,只要熟悉了目前为止我们介绍的运算符,这个问题就不难解决了。||优先级最低,其次是&&,接着是比较运算符(&gt;==等),最后是其他运算符。基于这些优先级顺序,我们在一般情况下最好还是尽量少用括号,比如说:
在混合使用布尔运算符和其他运算符的情况下,总是很难确定什么时候需要使用括号。实际上,只要熟悉了目前为止我们介绍的运算符,这个问题就不难解决了。`||`优先级最低,其次是`&&`,接着是比较运算符(`>``==`等),最后是其他运算符。基于这些优先级顺序,我们在一般情况下最好还是尽量少用括号,比如说:
```
1 + 1 == 2 && 10 * 10 > 50
@ -234,19 +257,19 @@ console.log(false ? 1 : 2);
// → 2
```
该运算符被称为条件运算符或者是三元运算符因为JavaScript语言中只有这唯一的一个三元运算符。条件运算符根据问号左侧条件的真伪从其余两个值中挑选出一个作为结果。当条件为真时选择中间的值当条件为假时选择右侧的值。
这个被称为条件运算符(或者有时候只是三元运算符,因为它是该语言中唯一的这样的运算符)。 问号左侧的值“挑选”另外两个值中的一个。 当它为真,它选择中间的值,当它为假,则是右边的值。
### 1.6 未定义
##
这里有两个特殊的值分别是null和undefined用于表示无意义的值。它们各自表示其自身含义除此之外不包含任何信息。
有两个特殊值,写成`null``undefined`,用于表示不存在有意义的值。 它们本身就是值,但它们没有任何信息。
在JavaScript语言中有许多操作都会产生无意义的值我们会在后面的内容中看到实例这些操作会得到undefined的结果仅仅只是因为每个操作都必须产生一个值。
JavaScript 语言中,有许多操作都会产生无意义的值(我们会在后面的内容中看到实例),这些操作会得到`undefined`的结果仅仅只是因为每个操作都必须产生一个值。
JavaScript语言设计上的问题导致了undefined和null含义存在些许不同在绝大部分情况下这种区别无关痛痒。如果你遇到了不得不区分这两个值的特殊情况那么我的建议是将这两个值视作可互换的值稍后对其进行详细介绍
`undefined``null`之间的意义差异是 JavaScript 设计的一个意外,大多数时候它并不重要。 在你实际上不得不关注这些值的情况下,我建议将它们视为几乎可互换的
### 1.7 自动类型转换
## 自动类型转换
本书的开篇我曾提到JavaScript可以处理任何程序即便程序的行为让人难以捉摸。我们可以通过以下表达式来看出这一点:
引言中,我提到 JavaScript 会尽可能接受几乎所有你给他的程序,甚至是那些做些奇怪事情的程序。 以下表达式很好地证明了这一点:
```
console.log(8 * null)
@ -261,11 +284,11 @@ console.log(false == 0)
// → true
```
当运算符作用在错误类型的值上时JavaScript会自动将其转换成自己期望的类型但是其规则却时常违背我们设计代码的初衷。这种操作称为强制类型转换。因此第一个表达式中的null变成了0而第二个表达式中的“5”则变成了5将字符串转换成数字。在第三个表达式中“+”运算符尝试进行字符串拼接而非数字加法因此将1转换成了“1”将数字转换成字符串)。
当运算符应用于类型“错误”的值时JavaScript 会悄悄地将该值转换为所需的类型,并使用一组通常不是你想要或期望的规则。 这称为类型转换。 第一个表达式中的`null`变为`0`,第二个表达式中的`"5"`变为`5`(从字符串到数字)。 然而在第三个表达式中,`+`在数字加法之前尝试字符串连接,所以`1`被转换为`"1"`(从数字到字符串)。
有些值无法显式地转换成数字比如说“five”或undefined就会产生NaN。而包含NaN的运算结果仍然是NaN所以当你意外地发现获得了NaN结果请检查一下是否发生了错误的类型转换。
某些不能明显映射为数字的东西(如`"five"``undefined`)转换为数字时,你会得到值`NaN``NaN`进一步的算术运算会产生`NaN`,所以如果你发现自己在一个意想不到的地方得到了它,需要寻找意外的类型转换。
当相同类型的值之间使用“==”符号进行比较时其运算结果很好预测除了NaN这种情况只要两个值相同则返回true。但如果类型不同JavaScript则会使用一套复杂难懂的规则来确定输出结果。在绝大多数情况下JavaScript只是将其中一个值转换成另一个值的类型。但如果运算符两侧存在null或undefined那么只有两侧均为null或undefined时结果才为true。
当相同类型的值之间使用`==`符号进行比较时,其运算结果很好预测:除了`NaN`这种情况,只要两个值相同,则返回`true`。但如果类型不同JavaScript 则会使用一套复杂难懂的规则来确定输出结果。在绝大多数情况下JavaScript 只是将其中一个值转换成另一个值的类型。但如果运算符两侧存在`null``undefined`,那么只有两侧均为`null``undefined`时结果才为`true`
```
console.log(null == undefined);
@ -274,37 +297,37 @@ console.log(null == 0);
// → false
```
上文提及的最后一点其实非常有用。当我们想确定一个值是否为真而非null或undefined时直接使用“==”(或“!=”)运算符来进行比较即可
这种行为通常很有用。 当你想测试一个值是否具有真值而不是`null``undefined`时,你可以用`==`(或`!=`)运算符将它与`null`进行比较
当你想要测试一个值是否严格等于false时会发生什么呢字符串与数字的布尔类型转换规则是JavaScript会将0、NaN和空字符串""视为false其他值视为true。因此诸如0==false和""==false之类的表达式都是true。在这种情况下如果我们不希望在比较的时候进行任何自动类型转换可以使用另外两个运算符===和!==。第一个运算符用于检测两个值是否严格相等,第二个运算符用于测试是否严格不等。所以""===false的结果是false正如我们预期
是如果你想测试某些东西是否严格为“false”呢 字符串和数字转换为布尔值的规则表明,`0``NaN`和空字符串(`""`)计为`false`,而其他所有值都计为`true`。 因此,像`'0 == false'``"" == false`这样的表达式也是真的。 当你不希望发生自动类型转换时,还有两个额外的运算符:`===``!==`。 第一个测试是否严格等于另一个值,第二个测试它是否不严格相等。 所以`"" === false`如预期那样是错误的
我建议使用三字符比较运算符来防止意外类型转换的发生,避免作茧自缚。但如果比较运算符两侧的值类型是相同的,那么使用较短的运算符也没有问题。
逻辑运算符的短路特性
### 逻辑运算符的短路特性
逻辑运算符&&和||可以使用一种特殊方式来处理不同类型的值。这两个运算符会将左侧的值转换成布尔类型,以决定如何进行后续操作,但返回左侧值还是返回右侧值,则取决于运算符和左侧转换结果
逻辑运算符`&&``||`以一种特殊的方式处理不同类型的值。 他们会将其左侧的值转换为布尔型,来决定要做什么,但根据运算符和转换结果,它们将返回原始的左侧值或右侧值
举例来说当左侧值可以被转换成true时||运算符会直接返回左侧的值,否则会返回右侧的值。当你希望以布尔值的方式来处理其他类型的值时,这种转换就派上用场了
例如,当左侧值可以转换为`true`时,`||`运算符会返回它,否则返回右侧值。 当值为布尔值时,这具有预期的效果,并且对其他类型的值做类似的操作
```
console.log(null || "user")
// → user
console.log("Karl" || "user")
// → Karl
console.log("Agnes" || "user")
// → Agnes
```
||运算符的这种功能可用于返回默认值。如果左侧表达式可能产生空值,那么右侧的表达式可以在左侧表达式为空时作为替代
我们可以此功能用作回落到默认值的方式。 如果你的一个值可能是空的,你可以把`||`和备选值放在它之后。 如果初始值可以转换为`false`,那么你将得到备选值
&&运算符工作方式与其相似但不相同。当左侧的值可以被转换成false时&&运算符会返回左侧值,否则返回右侧值。
`&&`运算符工作方式与其相似但不相同。当左侧的值可以被转换成`false`时,`&&`运算符会返回左侧值,否则返回右侧值。
这两种运算符的另一个重要特性是只有必要时才会计算右侧的表达式。以true||X为例无论X是什么即使X会进行一些可怕操作其结果都是true而且永远都不会计算X。false&&X这种情况也是一样只要左侧是falseX就会被忽略。这被称为短路计算
这两个运算符的另一个重要特性是,只在必要时求解其右侧的部分。 在`true || X`的情况下,不管`X`是什么 - 即使它是一个执行某些恶意操作的程序片段,结果都是`true`,并且`X`永远不会求值。 `false && X`也是一样,它是`false`的,并且忽略`X`。 这称为短路求值
条件运算符也会以类似方式工作。第一个表达式总会被计算,但第二个或第三个表达式只有在被选择时才会被计算
条件运算符以类似的方式工作。 在第二个和第三个值中,只有被选中的值才会求值
### 1.8 本章小结
## 本章小结
在本章中,我们介绍了JavaScript的四种类型的值数字、字符串、布尔值和未定义值。
在本章中,我们介绍了 JavaScript 的四种类型的值:数字,字符串,布尔值和未定义值。
通过输入值的名称(true、null或值13、"abc")就可以创建它们。你还可以通过运算符来对值进行合并和转换操作。本章已经介绍了算术二元运算符(+、–、*、/和%)、字符串连接符(+)、比较运算符(==、!=、===、!==、&lt;&gt;&lt;=和&gt;=)、逻辑运算符(&&和||和一些一元运算符表示负数表示逻辑非typeof用于查询值的类型)。
通过输入值的名称(`true``null`)或值(`13``"abc"`)就可以创建它们。你还可以通过运算符来对值进行合并和转换操作。本章已经介绍了算术二元运算符(`+````*``/``%`),字符串连接符(`+`),比较运算符(`==``!=``===``!==``<``>``<=``>=`),逻辑运算符(`&&``||`)和一些一元运算符(``表示负数,`!`表示逻辑非,`typeof`用于查询值的类型)。
利用这些知识,你完全可以编写出一个迷你计算器了,但仅限于此。在下一章中,我们准备将这些表达式应用在基本的程序当中。
这为你提供了足够的信息,将 JavaScript 用作便携式计算器,但并不多。 下一章将开始将这些表达式绑定到基本程序中。

BIN
img/1-0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB