一直没有静下心来,打扫 JavaScript 的边边角角,慢慢干吧~
第一部分 JavaScript 编程语言
什么是 JavaScript?
一种可以直接写在网页 HTML 中的脚本语言,在页面加载的时候自动执行。脚本以纯文本的形式提供,不需要编译,由 JavaScript 引擎直接解释运行。
引擎是如何工作的呢? 引擎很复杂,原理很简单:
- 引擎(如果是浏览器,则引擎被嵌入在其中)读取(“解析”)脚本;
- 然后,引擎将脚本转化(“编译”)为机器语言;
- 然后,机器代码快速地执行。
引擎会对流程中的每个阶段都进行优化。它甚至可以在编译的脚本运行时监视它,分析流经该脚本的数据,并根据获得的信息进一步优化机器代码。
JavaScript 的能力很大程度上取决于它运行的宿主环境(浏览器、Node),其本身没有直接访问操作系统的功能。
规范:ECMAScript® 2026 Language Specification
手册:JavaScript 参考 - JavaScript | MDN
我们几乎可以使用 <script> 标签将 JavaScript 程序插入到 HTML 文档的任何位置。
数据类型
在 JavaScript 中有 8 种基本的数据类型(7 种原始类型和 1 种引用类型)。
原始类型:其值只包含一个单独的内容(字符串、数字或者其他)
Number —— 代表整数和浮点数,常规数字、Infinity、-Infinity、NaN
BigInt —— 尾部 n 表示
String —— 字符串
Boolean —— true、false
null —— 空
undefined —— 通常用作未进行初始化时的默认值
Symbol —— 用于创建对象的唯一标示符
引用类型:用于储存数据集合和更复杂的实体
Object
我们可以通过 typeof 运算符 查看存储在变量中的数据类型(以字符串的形式返回)。
注意:
typeof是一个运算符,而不是函数!通常使用typeof x这种形式,有时也会使用typeof(x)—— 但这里的括号只是数学运算分组的括号。
|
|
Math 是一个提供数学运算的内建 object 。
typeof null 的结果为什么是 "object" ?不应该是 null 吗?这是官方承认的 typeof 的错误,为了兼容性保留下来的。
typeof alert 的结果为什么是 "function" ?并没有这个类型啊。原来是 typeof 会对函数区分对待,也是来自于 JavaScript 早期的问题,尽管这种行为原格来说是不正确的,但在实际编程中却非常方便。
大多数情况下,运算符和函数会自动将赋予它们的值转换为正确的类型。 如,alert 会自动将任何值都转换为字符串进行显示,算术运算符会将值转换为数字。
有三种常用的显示类型转换 —— String(value)、 Number(value) 、 Boolean(value) 。
其中需要注意的是, undefined 进行数字转换时输出为 NaN,而非 0 ;null 转换为 0 ;布尔转换时,对 "0" 和只有空格的字符串 " " 输出结果为 true 。
基础运算
算数运算中,+ 是比较特殊的,既可用于求和,又用于字符串的拼接 —— 只要任意一个运算元是字符串,另一个运算元也会被转成字符串。
二元 + 是唯一一个以这种方式支持字符串的运算符。 其他算术运算符只对数字起作用,并且总是将其运算元转换为数字。
+ 号还可以作为一元运算符使用,作用于单个值 —— 会将非数字的运算元转换为数字,效果和 Number(...) 相同。
|
|
在 JavaScript 中,所有运算符都会返回一个值。 = 就是赋值运算符,它也会返回一个值。
自增、自减 —— 初学者的必经之罪???
:: 其实是因为大多数垃圾教材只描述了结果,没有介绍原理。
自增/自减只能应用于变量,可以置于变量前,也可以置于变量后。我们以自增 ++ 为例,看一下“前置、后置”的区别。
¹ 如果自增、自减的值不会被使用,那么两者形式没有区别:
|
|
² 如果我们想要对变量进行自增操作,并且 需要立刻使用自增后的值,使用前置;如果想使用其自增之前的值,使用后置:
|
|
什么? 逗号也是运算符?
是的,逗号运算符能让我们处理多个表达式,使用 , 将它们分开。每个表达式都运行了,但是只有最后一个结果会被返回。
但要注意, 逗号运算符的优先级非常低! 比 = 号还低!
|
|
值的比较
所有比较运算符均返回布尔值。
当不同类型的值进行比较时,JavaScript 会首先将其转化为数字再判定大小。
需要注意的是,相等判断符号 == 两侧的值会优先转化为数字(除了 null 和 undefined),但 严格相等运算符 === 在进行比较时不会做任何的类型转换 。
其中,字符串就按字符逐个进行比较的 —— 按照字符的 Unicode 编码顺序比较。
呃,再来看看不让人省心的 null 和 undefined 。
null 和 undefined 在相等性检查 == 中不会进行任何的类型转换,它们有自己独立的比较规则 —— 除了它们之间互等之外,不会等于任何其他的值。
|
|
undefined 更特立独行,它在比较中被转换成了 NaN,它与任何值进行比较都会返回 false。
所以,在日常应用中,除了严格相等 === 外,其他但凡是有 undefined/null 参与的比较,我们需要格外小心。对于取值可能是 undefined/null 的变量,按需要分别检查它的取值情况。
条件分支
if (…) 语句会计算圆括号内的表达式,并将计算结果转换为布尔型。数字 0、空字符串 ""、null、undefined 和 NaN 都会被转换成 false,其他皆为真。
有时我们需要测试一个条件的几个变体。我们可以通过使用 else if 子句实现。如果前面的条件不符合,就会转到下一个条件,直到最后的 else (当然它是可选的)。
switch 语句为多分支选择的情况提供了一个更具描述性的方式,他它有至少一个 case 代码块和一个可选的 default 代码块。 其中,需要注意的是 case 后的匹配与 switch 中的传参的相等是 严格相等。
逻辑运算符
JavaScript 中有四个逻较运算符:|| 或、 && 与、 ! 非、 ?? 空值合并运算符。
虽然它们被称为逻辑运算符,但这些运算可以被应用于任意类型的值,而不仅仅是布尔值。它们的结果也同样可以是任意类型。
¹ || 或运算寻找第一个真值。
|
|
或运算符 || 做了如下事情:
- 从左到右依次计算操作数;
- 处理每一个操作数时,都将其转化为布尔值。如果结果是
true,就停止计算,返回这个操作数的初始值; - 如果所有的操作数都被计算过(都为
false),则返回最后一个操作数。
换句话说,一个或运算 || 的链,将返回第一个真值,如果不存在真值,就返回该链的最后一个值。
我们称上述这种方式为 短路求值(Short-circuit evaluation) 。常用来为变量设置默认值。
² && 与运算寻找第一个假值 —— 与运算返回第一个假值,如果没有假值就返回最后一个值。它的优先级比 || 高。
³ ! 非运算符接受一个参数,并返回相反的值。!! 常用来转化一个任意值为布尔值,等价于 Boolen(x) 。
⁴ 空值合并运算符 ?? 是什么呢? 它还什么用呢?
|
|
也就是说,如果第一个参数不是 null/undefined,则 ?? 返回第一个参数。否则,返回第二个参数。
它也用来设置默认值,但与 || 不同的是,它只是不允许值为 null/undefined ,但是允许其为 false、 0 、空字符串 '' 。
以上就是 ?? 出现的原因。
运算优先级: ! > && > || = ?? 。
注意:出于安全原因,JavaScript 禁止将 ?? 运算符与 && 和 || 运算符一起使用,除非使用括号明确指定了优先级。强用,会报错。
|
|
循环
循环 是一种重复运行同一代码的方法。
本文涵盖了基础的循环:while 、 do...while 和 for(..; ..; ..) 。
其他类型循环会放在以后的章节:
- 用于遍历对象属性的
for...in循环; - 用于遍历数组和可迭代对象的
for...of和iterables循环。
我们来看看最常用的 for 循环:
|
|
这里“计数”变量 i 是在循环中声明的,这叫做“内联”变量声明 —— 其作用域只存在于循环体内。
通常条件为假时,循环会终止,但我们可以使用 break 指令强止退出。也可以使用 continue 停止当前这一次迭代,并强制启到新一轮循环。
多层嵌套循环中,break 和 continue 都只能跳出其所在的循环,那么,当我们需要一次从多层循环中跳出来,怎么办呢?标签!
标签 是在循环之前带有冒号的标识符:
labelName: for (...) {
...
}
然后,使用 break <labelName> 语句跳出循环至标签处。