js-basics
— title: "JS 基础" —
参考资料:https://javascript.info/,JavaScript 高级程序设计。
https://wangdoc.com/javascript/index.html
## 基本语法
### 语句
以行(line)为单位执行语句(statement)。
```js var a = 1 + 4 ```
使用 `var` 命令声明变量 `a` ,然后将 `1 + 4` 的运算结果赋值给变量 `a` 。
`1 + 4` 是表达式(expression),指一个为了得到返回值的计算式。
> 语句和表达式的区别? > > 前者主要进行操作,基本情况下不需要返回值;后者则需要返回值。
语句以 `;` 结尾。分号前若无内容,JS 引擎视其为空语句。
### 变量
变量是对值(字符串、数字等)的引用。
```js var a = 1 ```
上述代码的运行流程:
- 声明变量 `a` : `var a`
- 建立 `a` 与 1 的引用关系: `a = 1`
第 2 步的专用术语是「赋值」。 `var` 是变量声明命令。
在未赋值时,变量的初始值为 `undefined` 。
声明变量一定要使用 var/let 命令,如果声明时不使用命令,很容易因声明过多全局变量而产生混乱。
如果未声明直接使用会报错: `Uncaught ReferenceError: a is not defined` 。
JS 是动态类型语言,变量声明为一种数据类型后,可以被轻易改变为其他数据类型:
```js var a = 1 a = 'hello' ```
对已声明变量的二次声明,如果没有赋值则二次声明无效;若赋值则变量的值得到更新。
#### 变量提升
JS 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这样的结果就是变量提升(hoisting)。
### 标识符
标识符(identifier)指的是用来识别各种值的合法名称。
最常见的是变量名和函数名。JS 的标识符对大小写敏感。
标识符命名规则:
- 第一个字符,可以是任意 Unicode 字母(包括英文字母和其他语言的字母),以及美元符号( `$` )和下划线( `_` )
- 第二个及以后的字符,除了 Unicode 字母、美元符号和下划线,还可以使用数字 `0-9`
> JS 中存在一些保留字,不能用作标识符:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、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
### 注释
两种,单行、多行
历史原因 JS 兼容 HTML 注释:
```js x = 1; <!– x = 2; –> x = 3; ```
以上只有 `x = 1` 有意义, `–>` 只有在行首才会被识别为单行注释。
### 区块
JS 使用大括号(curly braces),将多个相关的语句组合在一起,称为区块(block)。
对于 `var` 命令来说,JS 区块不构成独立的作用域;而 ES6 引入的 `let` 则具备这样的特点(独立作用域)。
### 条件语句
JS 提供 `if` 结构和 `switch` 结构,完成条件判断,即只有满足预设条件,才会执行相应的语句。
#### if 结构
`if` 结构先判断一个人表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。
注意, `if` 后面的表达式之中,不要混淆赋值表达式( `=` )、严格相等运算符( `===` )和相等运算符( `==` )。尤其是赋值表达式不具有比较作用。
错误代码:
```js var x = 1 var y = 2 if ((x = y)) { console.log(x) } ```
优先使用 `===` 。
#### if…else 结构
#### switch 结构
多个 `if…else` 连在一起使用时,可以用 `switch` 以一种更轻便的方式替代。
代码示例:
```js switch (fruit) { case 'banana': /… break case 'appple': /… break default: //… } ```
#### 三元运算符 ?:
```js (条件) ? 表达式 1 : 表达式 2 ```
它可以是 `if…else…` 的简写。
### 循环语句
循环语句用于重复执行某个操作。
#### while 循环
```js while (条件) 语句
// 或者 while (条件) 语句 ```
#### for 循环
```js for (初始化表达式; 条件; 递增表达式) { 语句 } ```
- 初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次
- 条件表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环
- 递增表达式(increment):每轮循环的最后一个操作通常用来递增循环变量
for 循环在 JS 引擎级别是如何实现的?
#### do…while 循环
与 `while` 的区别在于先运行一次循环体,然后判断循环条件。
```js do { 语句 } while (条件) ```
不论是 `i++` 还是 `++i` i 都是加 1,区别在于前一个表达式的结果是 i,后一个表达式的结果是 i+1。
#### break 语句和 continue 语句
`break` 语句和 `continue` 语句都具有跳转作用,可以让代码不按既有的顺序执行。
`break` 语句用于跳出代码块或循环; `continue` 语句用于立刻终止本轮循环,返回循环结构的头部,开始下一轮循环。
#### 标签(label)
JS 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签格式如下:
```js label: 语句 ```
标签可以是任意标识符,但不能是保留字,语句部分可以是任意语句。
标签通常与 `break` 语句和 `continue` 语句配合使用,跳出特定的循环。也可用于跳出代码块。
## 数据类型
JS 有 7 种数据类型:
- 数值(number)
- 字符串(string)
- 布尔值(boolean)
- `undefined`
- `null`
- 对象(object)
- Symbol(ES6 引入)
以上列出的 7 种,前三种是原始类型(primitive type)的值,是最基本的数据类型。对象被称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。 `undefined` 和 `null` 是两个特殊值。Symbol 的存在是为了解决属性名冲突。
对象又可以分成三个子类型:
- 狭义的对象(object)
- 数组(array)
- 函数(function)
狭义的对象和数组是两种不同的数据组合方式。函数是处理数据的方法,JS 视其为一种数据类型,可以赋值给变量。
> 函数被视为一种数据类型,能够赋值给变量这件事,为编程带来很大的灵活性,也为 JS 的函数式编程奠定基础。
### typeof 运算符
JS 有三种方法,可以确定一个值到底是什么类型。
- `typeof` 运算符
- `instanceof` 运算符
- `Object.prototype.toString` 方法
`null` 的类型是 `object` 。是历史原因,JS 最初的设计没考虑 `null` 只是把它作为 `object` 的一种特殊值。后来 `null` 独立出来,作为一种单独的数据类型,为了兼容以前的代码, `typeof null` 返回 `object` 就没法改变了。
### null,undefined 和布尔值
`null` 与 `undefined` 相似,都表示”不存在”。
存在它们的历史原因:
> 1995 年 JavaScript 诞生时,最初像 Java 一样,只设置了 `null` 表示"无"。根据 C 语言的传统, `null` 可以自动转为 `0` 。 > > 但是,JavaScript 的设计者 Brendan Eich,觉得这样做还不够。首先,第一版的 JavaScript 里面, `null` 就像在 Java 里一样,被当成一个对象,Brendan Eich 觉得表示”无”的值最好不是对象。其次,那时的 JavaScript 不包括错误处理机制,Brendan Eich 觉得,如果 `null` 自动转为 0,很不容易发现错误。 > > 因此,他又设计了一个 `undefined` 。区别是这样的: `null` 是一个表示”空”的对象,转为数值时为 `0` ; `undefined` 是一个表示"此处无定义"的原始值,转为数值时为 `NaN` 。
理解 `null` 和 `undefined` :
`null` 表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入 `null` ,表示该参数为空。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入 `null` ,表示未发生错误。
`undefined` 表示”未定义”,典型场景:
```js / 变量声明了,但没有赋值 var i console.log(i) / undefined
/ 调用函数时,应该提供的参数没有提供,该参数等于 undefined function f(x) { return x } console.log(f()) / undefined
/ 对象没有赋值的属性 var o = new Object() console.log(o.p) / undefined ```
布尔值代表”真”( `true` )和”假”( `false` )两个状态。
下列运算符返回布尔值:
- 前置逻辑运算符: `!` (Not)
- 相等运算符: `===` , `!==` , `==` , `!=`
- 比较运算符: `>` , `>=` , `<` , `<=`
JS 会将结果是布尔值但不是布尔值的位置,自动转换为布尔值。转换规则:除去以下六个值被转为 `false` ,其他值都视为 `true` 。
- `undefined`
- `null`
- `false`
- `0`
- `NaN`
- `""` 或 `''` (空字符串)
### 数值
#### 整数和浮点数
JS 内部,所有数字都是以 64 位浮点数形式储存,即使整数也是如此。
```js 1 =
1.0 // true ```
这也说明:JS 语言的底层没有整数,所有数字都是小数(64 位浮点数)。容易造成混淆的是 某些运算只有整数才能完成,此时 JS 会自动把 64 位浮点数,转成 32 整数,然后再进行运算。
浮点数不是精确的值,所以会出现以下情况:
```js 0.1 + 0.2 =
0.3 // false
0.3 / 0.1 // 2.9999999999999996 ```
#### 数值精度
根据国际标准 IEEE754,JS 浮点数的 64 个二进制位,从左边开始,是这样组成的:
- 第 1 位:符号位, `0` 表示正数, `1` 表示负数
- 第 2 位到第 12 位(共 11 位):指数部分
- 第 13 位到第 64 位(共 52 位):小数部分(即有效数字)
符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。
指数部分一共有 11 个二进制位,因此大小范围就是 0 到 2047。IEEE754 规定,如果指数部分的值在 0 到 2047 之间(不含两个端点),那么有效数字的第一位默认总是 1,不保存在 64 位浮点数中。也就是说,有效数字这时总是 `1.xx…xx` 形式,其中 `xx…xx` 的部分保存在 64 位浮点数中,最长可能为 52 位。因此 JS 提供的有效数字最长为 53 个二进制位。
```js (-1)符号位 * 1.xx…xx * 2指数部分 ```
上面公式是正常情况下(指数部分在 0 到 2047 之间),一个数在 JS 内部实际的表示形式。
精度最多只能到 53 个二进制位,这意味着,绝对值小于 2 的 53 次方的整数,即 \$-2^{53}$ 到 \$2^{53}$ ,都可以精确表示。
```js Math.pow(2, 53) // 9007199254740992
Math.pow(2, 53) + 1 // 9007199254740992
Math.pow(2, 53) + 2 // 9007199254740994
Math.pow(2, 53) + 3 // 9007199254740996
Math.pow(2, 53) + 4 // 9007199254740996 ```
由于 \$2^{53}$ 是一个 16 位的十进制数值,所以简单来说,JS 对 15 位的十进制数都可以精确处理。
#### 数值范围
根据标准,64 位浮点数的指数部分的长度是 11 个二进制位,意味着指数部分的最大值是 2047( \$2^{11}$ -1)。也就是说,64 位浮点数的指数部分的值最大为 2047,分出一半表示负数,则 JS 能够表示的数值范围为 \$2^{1024}$ 到 \$2^{-1024}$ (开区间),超出这个范围的数无法表示。
当 JS 因为某个正数过大而无法表示时,会返回 `Infinity` ;因为某个负数过小时,会返回 0。
JavaScript 提供 `Number` 对象的 `MAXVALUE` 和 `MINVALUE` 属性,返回可以表示的具体的最大值和最小值。
```js Number.MAXVALUE / 1.7976931348623157e+308 Number.MINVALUE / 5e-324 ```
#### 数值表示法
- 字面形式:十进制(1432)、八进制(074)、十六进制(0xff)
- 科学记数法:123e3、123e-3、-3.1e+12
JS 自动将数值转为科学记数法的两种情况:
- 小数点前的数字多于 21 位
- 小数点后的零多于 5 个
#### 数值的进制
使用字面量(literal)直接表示一个数值时,JS 对整数提供四种进制的表示方法:
- 十进制:没有前导 0 的数值
- 八进制:有前缀 `0o` 或 `00` 的数值,或者有前导 0、且只用到 0-7 八个阿拉伯数字的数值
- 十六进制:有前缀 `0x` 或 `0X` 的数值
- 二进制:有前缀 `0b` 或 `0B` 的数值
默认情况下,JS 内部会自动将八进制、十六进制、二进制转为十进制。如果八进制、十六进制、二进制中出现不属于该进制的数字,就会报错。
前到一般情况下,有前导 0 的数值会被视为八进制,但如果前导 0 后面有数字 `8` 和 `9` ,则该数值被视为十进制。
前导 0 表示八进制,处理时很容易造成混乱。ES5 的严格模式和 ES6,已经废除了这种表示法,但是浏览器为了兼容以前的代码,目前还继续支持这种表示法。
#### 特殊数值
一、 `+0` , `-0`
除了用作分母时,返回值不同外, `+0` 和 `-0` 在其他情况下使用时可以等效。
上面的代码之所以出现这样结果,是因为除以正零得到 `+Infinity` ,除以负零得到 `-Infinity` ,这两者是不相等的。
二、NaN
`NaN` 是 JS 的特殊值,表示非数字(Not a Number)。主要出现在将字符串解析成数字出错的场合。
```js 2 - 'x' // NaN ```
上述代码运行时,会自动将字符串 `x` 转为数值,但是由于 `x` 不是数值,所以最后得到结果 `NaN` 。
另外,一些数学函数的运算结果会出现 `NaN` 。
```js Math.acos(2) / NaN Math.log(-1) / NaN Math.sqrt(-1) // NaN ```
还有
```js 0 / 0 // NaN ```
需要注意的是, `NaN` 不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于 `Number` ,使用 `typeof` 运算符可以看得很清楚。
```js typeof NaN // "number" ```
NaN 的运算规则
`NaN` 不等于任何值,包括它自身。
数组的 `indexOf` 方法内部使用的是严格相等操作符,所以该方法对 `NaN` 不成立。
`NaN` 在布尔运算时被当作 `false` 。
`NaN` 与任何数(包括它自己)的运算,得到的都是 `NaN` 。
三、Infinity
`Infinity` 表示”无穷”,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非 0 数值除以 0,得到 `Infinity` 。
由于数值正向溢出(overflow)、负向溢出(underflow)和被 `0` 除,JavaScript 都不报错,所以单纯的数学运算几乎没有可能抛出错误。
`Infinity` 的运算规则
```js 5 * Infinity / Infinity 5 - Infinity / -Infinity Infinity / 5 / Infinity 5 / Infinity / 0 ```
```js 0 * Infinity / NaN 0 / Infinity / 0 Infinity / 0 // Infinity ```
```js Infinity - Infinity // NaN ```
Infinity 与 null 计算时,null 会转成 0,等同于与 0 的计算。
Infinity 与 undefined 计算,返回的都是 NaN。
#### 与数值相关的全局方法
- `parseInt()` 字符串->整数/NaN
- `parseFloat()` 字符串->浮点数
- `isNaN()` 判断一个值是否是 NaN
- `isFinite()` 返回布尔值,表示某个值是否为正常的数值
parseInt 会把字符串中是科学记数法形式的数字,当成普通字符串看待,也就是说: `e` 是字符而不是数字 10。
parseInt 接受第二个参数进行进制转换。参数取值为[2, 36]。
parseInt 第一个参数不是字符串,会被先转为字符串,再运算。注意:第一个参数是八进制数字的情况。
> JavaScript 不再允许将带有前缀 0 的数字视为八进制数,而是要求忽略这个 0。但是,为了保证兼容性,大部分浏览器并没有部署这一条规定。
parseFloat 会把空字符串转为 NaN。与 Number 函数有所不同。
isNaN 为 true 时,可能是字符串 / NaN。出于同样的原因,对象和数组,isNaN 也返回 true。但,空数组和只有一个数值元素的数组,isNaN 返回 false。原因是这些数组能够被 Number 函数转成数值。因此,使用 isNaN 之前要判断数据类型:
```js function myIsNaN(value) { return typeof value =
'number' && isNaN(value) } myIsNaN() ```
判断 NaN 更可靠的方法,利用 NaN 是唯一不等于自身的值的这个特点:
```js function myIsNaN(value) { return value !== value } myIsNaN(NaN) ```
> 除了 Infinity、-Infinity、NaN 和 undefined 这几个值会返回 false,isFinite 对于其他的数值都会返回 true。
### 字符串
#### 字符串定义
> 由于 HTML 语言的属性值使用双引号,所以很多项目约定 JavaScript 语言的字符串只使用单引号。
字符串默认只能写在一行内,分成多行将会报错。如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠。但是,输出的时候还是单行。注意,反斜杠的后面必须是换行符,而不能有其他字符(比如空格),否则会报错。连接运算符(+)可以连接多个单行字符串,将长字符串拆成多行书写,输出的时候也是单行。
如果想输出多行字符串,有一种利用多行注释的变通方法:
```js ;(function () { * line 1 line 2 line 3 * } .toString() .split('\n') .slice(1, -1) .join('\n')) ```
#### 转义
需要用反斜杠转义的特殊字符,主要有下面这些。
- `\0` :null( `0̆000` )
- ``̱ :后退键( `0̆008` )
- ``̑ :换页符( `0̆00C` )
- `\n` :换行符( `0̆00A` )
- ``̊ :回车键( `0̆00D` )
- ``͡ :制表符( `0̆009` )
- ``̌ :垂直制表符( `0̆00B` )
- `\'` :单引号( `0̆027` )
- `\"` :双引号( `0̆022` )
- `\\` :反斜杠( `0̆05C` )
反斜杠还有三种特殊用法。
(1) `\HHH`
反斜杠后面紧跟三个八进制数( `000` 到 `377` ),代表一个字符。 `HHH` 对应该字符的 Unicode 码点,比如 `\251` 表示版权符号。显然,这种方法只能输出 256 种字符。
(2) `\xHH`
`\x` 后面紧跟两个十六进制数( `00` 到 `FF` ),代表一个字符。 `HH` 对应该字符的 Unicode 码点,比如 `\xA9` 表示版权符号。这种方法也只能输出 256 种字符。
(3) `\uXXXX`
``̆ 后面紧跟四个十六进制数( `0000` 到 `FFFF` ),代表一个字符。 `XXXX` 对应该字符的 Unicode 码点,比如 `0̆0A9` 表示版权符号。
如果在非特殊字符前面使用反斜杠,则反斜杠会被省略。
如果字符串的正常内容之中,需要包含反斜杠,则反斜杠前面需要再加一个反斜杠,用来对自身转义。
#### 字符串与数组
字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从 0 开始)。
如果方括号中的数字超过字符串的长度,或者方括号中根本不是数字,则返回 undefined。
但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符。
#### length 属性
同样无法改变
#### 字符集
JS 使用 Unicode 字符集,JS 引擎内部,所有字符都用 Unicode 表示。
还能在 JS 中使用 Unicode 码点表示字符。
每个字符在 JS 内部都是以 16 位(即两个字节)的 UTF-16 格式存储。换句话说,JS 的单位字符长度固定为 16 位长度,即 2 个字节。
但是,UTF-16 有两种长度:对于码点在 `U+0000` 到 `U+FFFF` 之间的字符,长度为 16 位(即 2 个字节);对于码点在 `U+10000` 到 `U+10FFFF` 之间的字符,长度为 32 位(即 4 个字节),而且前两个字节在 `0xD800` 到 `0xDBFF` 之间,后两个字节在 `0xDC00` 到 `0xDFFF` 之间。
JS 对 UTF-16 的支持并不完整。历史遗留问题,导致 JS 无法识别超出 2 字节的 Unicode 字符。这就导致:对于码点在 `U+10000` 到 `U+10FFFF` 之间的字符,JS 总是认为它们是两个字符( `length` 属性为 2)。因此,在涉及到字符串长度问题时要小心,可能 JS 返回的字符串长度不是字符串的长度。
#### Base64 转码
当文本包含一些不可打印的符号时,需要通过 Base64 转码,将它们转换成可打印字符,让其可打印。
另一个使用场景:以文本格式传递二进制数据。
Base64 是一种编码方法。可以将任意值转成 0~9、A~Z、a~z、 `+` 和 `/` 这 64 个字符组成的可打印字符。产生这种编码方法的主要目的不是为了加密,而是为了不出现特殊字符,简化程序的处理。
JS 原生提供两个 Base64 相关的方法。
- `btoa()` :任意值转为 Base64 编码
- `atob()` :Base64 编码转为原来的值
注意,这两种方法不支持非 ASCII 字符。
要将非 ASCII 码字符转为 Base64 编码,必须中间插入一个转码环节,在使用这个方法。
```js function b64Encode(str) { return btoa(encodeURIComponent(str)) } b64Encode('你好,世界') function b64Decode(str) { return decodeURIComponent(atob(str)) } b64Decode('JUU0JUJEJUEwJUU1JUE1JUJEJUVGJUJDJThDJUU0JUI4JTk2JUU3JTk1JThD') ```
### 对象
对象,是键值对的集合。
对象的所有键名都是字符串/Symbol。键名也称为属性。键值可以是任何数据类型,如果值为函数,可以把该属性称为方法。想到 Vue 的一些代码。
属性可以在任何时刻创建。
#### 对象的引用
不同的变量名指向同一个对象,它们都是该对象的引用,指向同一个内存地址。
这种类型的引用仅限于对象。如果变量引用原始数据类型,附加的变量只是初始变量的拷贝。
#### 表达式还是语句?
```js { foo: 123 } ```
JS 引擎认为所有开头有大括号的语句都是代码块。如果想表示对象,需要这样:
```js ;({ foo: 123 }) ```
#### 属性的操作
读、写、删除
读: `.` / `[]`
方括号内部可使用表达式
数字会自动转为字符串,数字属性不能使用 `.` 获取属性值。
写:直接对属性赋新值
查看对象所有属性: `Object.keys` 。
删除:delete
删除返回 true 并不能确定被删除属性存在。
返回 false 说明该属性不可删除。
delete 只能删除对象的本身属性,不能删除继承属性。
属性是否存在? `in`
属性遍历:for…in
对象属性的可遍历性。
with 语句会产生全局变量,最好不用。
### 函数
#### 函数声明
(1)function
(2)函数表达式
(3) `Function` 构造函数
#### 函数的重复声明
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
```js function f() { console.log(1) } f() // 2
function f() { console.log(2) } f() // 2 ```
函数名提升
#### 圆括号运算符、return 语句和递归
函数调用自身即是递归(recursion)。
#### 第一等公民
凡是能使用值的地方,都能使用函数。
```js function add(x, y) { return x + y } var op = add
function a(op) { return op } a(op)(1, 1) ```
最后一行为什么这样写也可以?正常写法不应该是 `a(op(1,1))` 吗?
#### 函数名提升
JavaScript 引擎将函数名视同变量名,所以采用 function 命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
#### 函数的属性和方法
(1)name
返回函数名/变量名
(2)length
返回传入参数个数
(3)toString()
返回函数源码字符串
对于那些原生的函数,toString()方法返回 `function (){[native code]}` 。
函数内部的注释也会返回。可以利用这一点实现多行字符串。
```js var multiline = function (fn) { var arr = fn.toString().split('\n') return arr.slice(1, arr.length - 1).join('\n') }
function f() { * 这是一个 多行注释* } multiline(f) ```
#### 函数作用域
作用域(scope)指变量存在的范围。在 ES5 规范中,JS 只有两种作用域:全局作用域和函数作用域;ES6 引入块级作用域。
函数自身也是一个作用域。
函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。
很容易犯错的一点是,如果函数 A 调用函数 B,却没考虑到函数 B 不会引用函数 A 的内部变量。
函数体内部声明的函数,作用域绑定函数体内部。
#### 参数
参数可省略。
参数传递方式:
- 按值传递(值是原始类型)
- 按址传递(值是复合类型)
同名参数
arguments 对象可以读取函数传入的参数。需要注意的是,虽然 arguments 很像数组,但它是一个对象。数组专有的方法(比如 slice 和 forEach),不能在 arguments 对象上直接使用。arguments 对象带有一个 callee 属性,返回它所对应的原函数。
#### 闭包
```js function f1() { var n = 999 function f2() { console.log(n) } return f2 }
var result = f1() result() // 999 ```
上面代码中,函数 f2 就在函数 f1 内部,这时 f1 内部的所有局部变量,对 f2 都是可见的。但是反过来就不行,f2 内部的局部变量,对 f1 就是不可见的。这就是 JavaScript 语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
闭包就是函数 f2,即能够读取其他函数内部变量的函数。
```js function createIncrementor(start) { return function () { return start++ } }
var inc = createIncrementor(4) inc() inc() ```
为什么闭包能够返回外层函数的内部变量?原因是闭包(上例的 inc)用到了外层变量(start),导致外层函数(createIncrementor)不能从内存释放。只要闭包没有被垃圾回收机制清除,外层函数提供的运行环境也不会被清除,它的内部变量就始终保存着当前值,供闭包读取。
闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
闭包的另一个用处,是封装对象的私有属性和私有方法。
```js function Person(name) { var age function setAge(n) { age = n } function getAge() { return age } return { name: name, getAge: getAge, setAge: setAge, } }
var p1 = Person('Jim') p1.setAge(23) p1.getAge() p1 ```
注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
#### 立即调用的函数表达式
Immediately-Invoked Function Expression
```js var f = (function () { return 1 })() // 或者这样写 (function f() {return 1}) 或者 (function f() {return 1}()) 或者 (function f() {return 1})() f ```
通常情况下,只对匿名函数使用这种”立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
```js / 写法1 var tmp = newData processData(tmp) storeData(tmp)( / 写法2 (function () { var tmp = newData processData(tmp) storeData(tmp) })(), ) ```
上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。
#### eval
eval 命令接受一个字符串作为参数,并将这个字符串当作语句执行。
如果参数非字符串,原样返回。
eval 没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。由于这个原因,eval 有安全风险。为了防止这种风险,JavaScript 规定,如果使用严格模式,eval 内部声明的变量,不会影响到外部作用域。不过,即使在严格模式下,eval 依然可以读写当前作用域的变量。
总之,eval 的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,一般不推荐使用。通常情况下,eval 最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的 JSON.parse 方法。
eval 的别名调用
```js var m = eval m('var x = 1') x ```
为了保证 eval 的别名不影响代码优化,JavaScript 的标准规定,凡是使用别名执行 eval,eval 内部一律是全局作用域。
## 参考资料
- John Dalziel, [The race for speed part 4: The future for JavaScript](http://creativejs.com/2013/06/the-race-for-speed-part-4-the-future-for-javascript/) | [The race for speed part 2: How JavaScript compilers work](http://creativejs.com/2013/06/the-race-for-speed-part-2-how-javascript-compilers-work/)
- http://kangax.github.io/compat-table/es6/ JS 标准兼容性表
- Dr. Axel Rauschmayer, [Basic JavaScript for the impatient programmer](https://2ality.com/2013/06/basic-javascript.html) | [Basic JavaScript for the impatient programmer](https://2ality.com/2013/06/basic-javascript.html) | [Improving the JavaScript typeof operator](https://2ality.com/2011/11/improving-typeof.html) | [Categorizing values in JavaScript](https://2ality.com/2013/01/categorizing-values.html) | [How numbers are encoded in JavaScript](https://2ality.com/2012/04/number-encoding.html) | [Object properties in JavaScript](https://2ality.com/2012/10/javascript-properties.html) | [JavaScript's with statement and why it's deprecated](https://2ality.com/2011/06/with-statement.html) | [Evaluating JavaScript code via eval() and new Function()](https://2ality.com/2014/01/eval.html) | [Arrays in JavaScript](https://2ality.com/2012/12/arrays.html) | [JavaScript: sparse arrays vs. dense arrays](https://2ality.com/2012/06/dense-arrays.html) | [What is {} + {} in JavaScript?](https://2ality.com/2012/01/object-plus-object.html) | [JavaScript quirk 1: implicit conversion of values](https://2ality.com/2013/04/quirk-implicit-conversion.html) | [A meta style guide for JavaScript](https://2ality.com/2013/07/meta-style-guide.html) | [Automatic semicolon insertion in JavaScript](https://2ality.com/2011/05/semicolon-insertion.html) | [What JavaScript would be like with significant newlines](https://2ality.com/2011/11/significant-newlines.html) | [The JavaScript console API](https://2ality.com/2013/10/console-api.html) | [Protecting objects in JavaScript](https://2ality.com/2013/08/protecting-objects.html) | [JavaScript: an overview of the regular expression API](https://2ality.com/2011/04/javascript-overview-of-regular.html) | [The flag /g of JavaScript's regular expressions](https://2ality.com/2013/08/regexp-g.html) | [JavaScript's JSON API](https://2ality.com/2011/08/json-api.html) | [JavaScript's this: how it works, where it can trip you up](https://2ality.com/2014/05/this.html) | [JavaScript properties: inheritance and enumerability](https://2ality.com/2011/07/js-properties.html) | [JavaScript: Why the hatred for strict mode?](https://2ality.com/2011/10/strict-mode-hatred.html) | [JavaScript's strict mode: a summary](https://2ality.com/2011/01/javascripts-strict-mode-summary.html) | [ECMAScript 6 promises (1/2): foundations](https://2ality.com/2014/09/es6-promises-foundations.html) | [Speaking JavaScript](http://speakingjs.com/es5/index.html)(ES5) | [Exploring JS: JavaScript books for programmers](https://exploringjs.com/)(ES6 and other versions): The Past, Present, and Future of JavaScript
- [Happy 18th Birthday JavaScript! A look at an unlikely past and bright future.](https://www.balena.io/blog/happy-18th-birthday-javascript/)
- Humphry, [JavaScript 中 Number 的一些表示上/下限](https://segmentfault.com/a/1190000000407658)
- Mathias Bynens, [JavaScript's internal character encoding: UCS-2 or UTF-16?](https://mathiasbynens.be/notes/javascript-encoding) | [JavaScript has a Unicode problem](https://mathiasbynens.be/notes/javascript-unicode)
- MDN Web Docs, [btoa()](https://developer.mozilla.org/en-US/docs/Web/API/btoa) | [Regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) | [Expressions and operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators) | [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) | [Strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) | [setTimeout()](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) | [Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) | [Window.postMessage()](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) | [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) | [Location](https://developer.mozilla.org/en-US/docs/Web/API/Location) | [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) | [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- Lakshan Perera, [Revisiting JavaScript Objects](https://www.laktek.com/2012/12/29/revisiting-javascript-objects/)
- Angus Croll, [The Secret Life of JavaScript Primitives](https://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/)
- Ben Alman, [Immediately-Invoked Function Expression (IIFE)](https://benalman.com/news/2010/11/immediately-invoked-function-expression/)
- Mark Daggett, [Functions Explained](https://web.archive.org/web/20160911170816/http://markdaggett.com/blog/2013/02/15/functions-explained)
- Juriy Zaytsev, [Named function expressions demystified](https://kangax.github.io/nfe/) | [Global eval. What are the options?](http://perfectionkills.com/global-eval-what-are-the-options/) | [How ECMAScript 5 still does not allow to subclass array](http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/) | [Understanding delete](http://perfectionkills.com/understanding-delete/)
- Marco Rogers polotek, [What is the arguments object?](https://web.archive.org/web/20160322233747/http://docs.nodejitsu.com:80/articles/javascript-conventions/what-is-the-arguments-object)
- Felix Bohm, [What They Didn't Tell You About ES5's Array Extras](https://code.tutsplus.com/tutorials/what-they-didnt-tell-you-about-es5s-array-extras--net-28263)
- Michal Budzynski, [JavaScript: The less known parts. Bitwise Operators.](https://michalbe.blogspot.com/2013/03/javascript-less-known-parts-bitwise.html) | [JavaScript: The less known parts. DOM Mutations.](https://michalbe.blogspot.com/2013/04/javascript-less-known-parts-dom.html)
- Benjie Gillam, [Quantum JavaScript?](http://www.benjiegillam.com/2013/06/quantum-javascript/)
- Jani Hartikainen, [JavaScript Errors and How to Fix Them](https://davidwalsh.name/fix-javascript-errors)
- Eric Elliott, Programming JavaScript Applications, Chapter 2. JavaScript Style Guide, O'Reilly, 2014
- Rod Vagg, [Semicolons, Objectively](https://web.archive.org/web/20170517132624/http://dailyjs.com/post/semicolons)
- [Chrome Devtools](https://developer.chrome.com/docs/devtools/) | [Console Utilities API reference](https://developer.chrome.com/docs/devtools/console/utilities/)
- Matt West, [Mastering The Developer Tools Console](https://blog.teamtreehouse.com/mastering-developer-tools-console)
- [Firefox Devtools - Console API](https://developer.mozilla.org/en-US/docs/Web/API/console)
- Marius Schulz, [Advanced JavaScript Debugging with console.table()](https://mariusschulz.com/blog/advanced-javascript-debugging-with-console-table)
- Jon Bretman, [Type Checking in JavaScript](https://web.archive.org/web/20201112014755/https://badoo.com/techblog/blog/2013/11/01/type-checking-in-javascript/)
- Cody Lindley, [Thinking About ECMAScript 5 Parts](https://web.archive.org/web/20150424141701/http://tech.pro:80/tutorial/1671/thinking-about-ecmascript-5-parts)
- Bjorn Tipling, [Advanced objects in JavaScript](https://web.archive.org/web/20140828092110/http://bjorn.tipling.com/advanced-objects-in-javascript)
- Javier Márquez, [Javascript properties are enumerable, writable and configurable](http://arqex.com/967/javascript-properties-enumerable-writable-configurable)
- Sella Rafaeli, [Native JavaScript Data-Binding](https://www.sellarafaeli.com/blog/native_javascript_data_binding)
- Lea Verou, [Copying object properties, the robust way](https://lea.verou.me/2015/08/copying-properties-the-robust-way/)
- Nicolas Bevacqua, [Fun with JavaScript Native Array Functions](https://web.archive.org/web/20131130130119/http://flippinawesome.org/2013/11/25/fun-with-javascript-native-array-functions/)
- Ariya Hidayat, [JavaScript String: substring, substr, slice](https://ariya.io/2014/02/javascript-string-substring-substr-slice)
- Rakhitha Nimesh, [JavaScript Date Object: The Beginner's Guide to JavaScript Date and Time](https://www.sitepoint.com/beginners-guide-to-javascript-date-and-time/)
- [Date and time - javascript.info](https://javascript.info/date)
- Sam Hughes, [Learn regular expressions in about 55 minutes](https://qntm.org/re_en)
- Jim Cowart, [What You Might Not Know About JSON.stringify()](https://web.archive.org/web/20130201093823/http://freshbrewedcode.com/jimcowart/2013/01/29/what-you-might-not-know-about-json-stringify/)
- Marco Rogers, [What is JSON?](https://web.archive.org/web/20110925123121/http://docs.nodejitsu.com/articles/javascript-conventions/what-is-json)
- Jonathan Creamer, [Avoiding the "this" problem in JavaScript](https://web.archive.org/web/20130406213730/http://tech.pro/tutorial/1192/avoiding-the-this-problem-in-javascript)
- Erik Kronberg, [Bind, Call and Apply in JavaScript](https://web.archive.org/web/20131113145406/https://variadic.me/posts/2013-10-22-bind-call-and-apply-in-javascript.html)
- [JavaScript Modules: A Beginner's Guide](https://www.freecodecamp.org/news/javascript-modules-a-beginner-s-guide-783f7d7a5fcc)
- Douglas Crockford, [Strict Mode Is Coming To Town](https://web.archive.org/web/20101216151915/http://www.yuiblog.com/blog/2010/12/14/strict-mode-is-coming-to-town/)
- Sebastian Porto, [Asynchronous JS: Callbacks, Listeners, Control Flow Libs and Promises](https://sporto.github.io/blog/2012/12/09/callbacks-listeners-promises/)
- Rhys Brett-Bowen, [Promises/A+ - understanding the spec through implementation](https://modernjavascript.blogspot.com/2013/08/promisesa-understanding-by-doing.html)
- Marc Harter, [Promise A+ Implementation](https://gist.github.com//wavded/5692344)
- Matt Podwysocki, Amanda Silver, [Asynchronous Programming in JavaScript with "Promises"](https://web.archive.org/web/20110923162652/http://blogs.msdn.com/b/ie/archive/2011/09/11/asynchronous-programming-in-javascript-with-promises.aspx)
- Bryan Klimt, [What's so great about JavaScript Promises?](https://web.archive.org/web/20130203032915/http://blog.parse.com/2013/01/29/whats-so-great-about-javascript-promises/)
- Jake Archibald, [JavaScript Promises: an introduction](https://web.dev/promises/)
- Mikito Takada, [Control flow](http://book.mixu.net/node/ch7.html)
- Craig Buckler, [How to Translate from DOM to SVG Coordinates and Back Again](https://www.sitepoint.com/how-to-translate-from-dom-to-svg-coordinates-and-back-again/)
- Paul Kinlan, [Detect DOM changes with Mutation Observers](https://developers.google.com/web/updates/2012/02/Detect-DOM-changes-with-Mutation-Observers)
- Tiffany Brown, [Getting to Know Mutation Observers](https://dev.opera.com/articles/mutation-observers-tutorial/)
- Jeff Griffiths, [DOM MutationObserver – reacting to DOM changes without killing browser performance.](https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/)
- Addy Osmani, [Detect, Undo And Redo DOM Changes With Mutation Observers](https://addyosmani.com/blog/mutation-observers/)
- Ryan Morr, [Using Mutation Observers to Watch for Element Availability](http://ryanmorr.com/using-mutation-observers-to-watch-for-element-availability/)
- Jake Archibald, [Deep dive into the murky waters of script loading](https://www.html5rocks.com/en/tutorials/speed/script-loading/)
- Remy Sharp, [Throttling function calls](https://remysharp.com/2010/07/21/throttling-function-calls/)
- Ayman Farhat, [An alternative to Javascript's evil setInterval](https://www.thecodeship.com/web-development/alternative-to-javascript-evil-setinterval/)
- Ilya Grigorik, [Script-injected "async scripts" considered harmful](https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/)
- Daniel Imms, [async vs defer attributes](https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html)
- Craig Buckler, [Load Non-blocking JavaScript with HTML5 Async and Defer](https://www.sitepoint.com/non-blocking-async-defer/)
- Domenico De Felice, [How browsers work](https://domenicodefelice.blogspot.com/2015/08/how-browsers-work.html)
- [Using the Same-Site Cookie Attribute to Prevent CSRF Attacks](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/)
- [SameSite cookies explained](https://web.dev/samesite-cookies-explained/)
- [Tough Cookies](https://scotthelme.co.uk/tough-cookies/)
- [Cross-Site Request Forgery is dead!](https://scotthelme.co.uk/csrf-is-dead/)
- Jakub Jankiewicz, [Cross-Domain LocalStorage](https://jcubic.wordpress.com/2014/06/20/cross-domain-localstorage/)
- David Baron, [setTimeout with a shorter delay](https://dbaron.org/log/20100309-faster-timeouts)
- Monsur Hossain, [Cross-Origin Resource Sharing (CORS)](https://web.dev/cross-origin-resource-sharing/)
- https://frontendian.co/cors
- Grzegorz Mirek, [Do You Really Know CORS?](https://web.archive.org/web/20180929153256/http://performantcode.com/web/do-you-really-know-cors)
- [Introducing the HTML5 storage APIs](https://web.archive.org/web/20111011045651/http://www.adobe.com/devnet/html5/articles/html5-storage-apis.html)
- [Getting Started with LocalStorage](https://web.archive.org/web/20130105051751/http://codular.com/localstorage)
- [Introducing the HTML5 Hard Disk Filler™ API](https://feross.org/fill-disk/)
- [Inter-window messaging using localStorage](https://web.archive.org/web/20130311215851/http://bens.me.uk/2013/localstorage-inter-window-messaging)
- [Why does Internet Explorer fire the window "storage" event on the window that stored the data?](https://stackoverflow.com/q/18265556)
- [localStorage eventListener is not called](https://stackoverflow.com/q/5370784)
- [Easy URL Manipulation with URLSearchParams](https://developers.google.com/web/updates/2016/01/urlsearchparams)
- Thoriq Firdaus, [HTML5 Form Validation With the "pattern" Attribute](https://webdesign.tutsplus.com/tutorials/html5-form-validation-with-the-pattern-attribute--cms-25145)
- Raymond Camden, [Working With IndexedDB](https://code.tutsplus.com/tutorials/working-with-indexeddb--net-34673) | [Working With IndexedDB - Part 2](https://code.tutsplus.com/tutorials/working-with-indexeddb-part-2--net-35355) | [Working With IndexedDB - Part 3](https://code.tutsplus.com/tutorials/working-with-indexeddb-part-3--net-36220)
- Tiffany Brown, [An Introduction to IndexedDB](https://dev.opera.com/articles/introduction-to-indexeddb/)
- David Fahlander, [Breaking the Borders of IndexedDB](https://hacks.mozilla.org/2014/06/breaking-the-borders-of-indexeddb/)
- [HTML5 - IndexedDB](https://web.archive.org/web/20150716002214/http://www.tutorialspoint.com/html5/html5_indexeddb.htm)