ES6入门(阮一峰)chapter1~9(不包括正则部分)

360 阅读17分钟

1.ES6简介

1.1 ES6与ES5的关系

前者是基于后者改进的一个版本,ES6的第一个版本发布于2015年六月,现在泛指ES5.1后的语言标准。

1.2 ES的历史

ES1.0发布于1997年,后续就有2.0、3.0版本,因为4.0版本改动太多,被一分为二一部分演变成ES6,还有一部分还在开发中。而发布于2009年12月的ES5则与ES3基本保持兼容,没有说明特别大的改动。

1.3 Babel转码器

Babel可以将ES6的代码转为ES5代码,避免出现和老版本的浏览器不兼容的问题。

指令:npm install --save-dev @babel/core。(根据具体使用情况查询指令,先安装cnpm)

2.let和const

2.1 let命令

ES6新增let命令来声明变量。与var不同的是,let只在块级作用域内有效,而且不存在变量提升,会出现暂时性死区,并且不允许重复声明

2.1.1 块级作用域

ES5只有全局作用域和函数作用域(闭包的原理就是函数作用域),没有块级作用域,这样就导致了类似 if 这种块级操作内部的变量覆盖外层的同名变量,这是第一种情况,还有一种情况就是 for 中用来计数的变量被泄露为了全局变量。

ES6的块级作用域,即使无限嵌套,内部变量也只能在被定义的那一层访问。有了块级作用域,就再需要匿名函数来形成闭包防止变量泄露了。

与函数声明的关系。 在ES5中,函数只能在顶层作用域或者函数作用域中声明的,但是在ES6中,允许在块级作用域内声明函数,函数声明类似于var,存在变量(函数?)提升。此外,函数声明也会提升到所在块级作用域的头部。ES6的块级作用域必须有大括号。

2.1.2 变量提升

变量提升(let没有没有没有!只有var有),即变量可以在声明之前使用,即使我在代码的最后一行声明一个变量,我在代码的第一行依旧可以使用,只是此时因为没有赋值会显示为undefined。

2.1.3 暂时性死区

代码块内,使用let声明变量之前,该变量不可用。这也意味着typeof不再是一个百分百安全的操作。如果在用let声明某变量之前,使用typeof,运行时就会抛出引用错误(ReferenceError),但如果这个变量没有被声明,反而不会报错,只会显示undefined。

2.1.4 重复声明

在同一个作用域中不能重复声明一个变量,函数的参数也不可以在函数内部直接声明,例如function(args){ let args;}就是错的,但是可以在函数内部的块级作用域声明,例如function(args){{let args;}}。

2.2 const命令

类似其他的语言,const一般用来声明一个常量,例如const PI=3.14。和let一样,const也是有块级作用域的,但是当const定义的是一个对象时,保存的其实就是一个地址,此时是可以对该对象内部的值进行修改的,但是不能改变地址指向另一个对象。

如果需要将一个对象锁定为一个内部不能修改的常量,就可以是使用Object.freeze()方法。

ES6声明变量在ES的var和function命令上,还添加了let、const、import和class命令,一共有六种声明变量的方法。

2.3 顶层对象的属性

在浏览器环境下,顶层对象是window对象。

image-20211117145403651.png 而在node环境下,顶层对象是global对象。

image-20211117145307731.png 在ES5中,顶层对象的属性与全局变量都是等价的。所以顶层对象的属性赋值与全局变量的赋值都是一个意思。而ES6中,由于let、const、var的存在,全局变量逐渐与顶层对象的属性分离。

2.4 This的指向

全局环境中,this会返回顶层对象。但是,Node.js模块中的this返回的是当前模块,ES6模块的this返回undefined。

函数中的this,如果函数不是作为对象的方法运行,则this指向顶层对象,但是严格模式下,this还是会返回undefined。

任何情况下,new Function('return this')() 都会返回全局对象。但是如果浏览器用了CSP(content security policy),则eval、new Function方法可能无法使用。

3.变量的解构赋值

3.1 数组的解构赋值
3.1.1 基本用法

只要等号两边的模式相同,左边的变量就会被赋予对应的值。

例如:

let [a,b,c] = [1,2,3];

let [foo,[[bar],baz]] = [1,[[2],3]];

let [,,third] = ["a","b","c"];

let [a, ...b] = [1,2,3,4]//a=1,b=4

结构不成功变量的值就会是undefined。

不完全解构:等号左边的模式,只匹配了一部分右边的数组,这种情况下虽然没有完全解构,但是也可以解构成功。(如果等号右边不是可遍历的解构(没有Iteractor接口),解构就会失败。)

3.1.2 默认值。

左边需要赋值的变量可以指定默认值,只有匹配的右边元素为undefined时,默认值才会生效。如果默认值是一个表达式,则这个表达式时惰性求值的,只有在用到的时候才会求值。

3.2 对象的解构赋值
3.2.1 基本用法

对象解构需要变量与属性同名才能取值,如果没有同名属性,则会取undefined。

如果变量名与属性名不一致,则等号左边的变量需要是和右边key同名的key值对应的value。

image-20211117151701799.png

嵌套结构的解构。

image-20211117152034373.png

如果下面的赋值是let {p:[x,{y}]} = obj,此时的p就是一个模式而不是变量。

对象的解构赋值可以取到继承的属性。

image-20211117152334656.png

3.2.2 默认值

同数组,默认值要生效则对象的属性必须严格等于undefined。

3.2.3 注意点

(1) 已声明的变量用于解构赋值。

错误用法:let x; {x} = {x:1};

正确用法:let x;({x} = {x:1});

(2) 等号左边的模式可以为空,不会报错,但是没有什么意义。

(3) 数组本质就是特殊的对象,可以对数组进行对象属性的解构。

3.3 字符串的解构赋值

除了可以let [a,s,d,f,g] = "hello"这样使用,还可以let {length: len} = "hello"这样使用。

3.4 数值和布尔值的解构赋值

此时,等号右边如果也是数值/布尔值,就会先转成对象,赋值后还需要数据类型的转化。但是用于null、undefined不能转化为对象,所以无法对他们进行解构赋值。

3.5 函数参数的解构赋值(比较简单,不赘述)
3.6 圆括号的问题(少用圆括号)

3.2.3的(1),如果有可能导致解构的歧义,解构变成对象赋值,就使用圆括号,但是不推荐使用。

不能使用圆括号的情况:

(1)变量声明语句。

(2)函数参数。

(3)赋值语句的模式。

可以使用圆括号的情况:

赋值语句的非模式部分。

3.7 为什么要解构赋值
  1. 变换变量的值。(swap)
  2. 从函数返回多个值。
  3. 函数参数的定义。
  4. 提取JSON数据。
  5. 函数参数的默认值。
  6. 遍历Map结构。(let [key] of map、let [,value] of map)
  7. 输入模块的指定方法。(require、import的应用。)

4.字符串的扩展

4.1 字符串的Unicode表示法

允许用\uxxxx(\u0000~\uFFFF)来表示一个字符,原本超过这个范围就必须用两个双字节的形式表示。如果超过0xFFFF,js就会将其拆分,例如\u20BB7就会拆成\u20BB+7,\U20BB是一个不可打印的字符,此时就会显示一个空格后面跟一个7。

image-20211117154738178.png

但在ES6中,即使超过这个范围,将码点放入大括号,就能正确解读该字符。

image-20211117154959285.png

所以ES6中,表示一个字符就有六种方法。

image-20211117155036834.png

4.2 字符串的遍历器接口

可以用for ... of来遍历一个字符串了,而且该遍历器还可以识别大于0xFFFF的码点,而传统的for循环无法识别这样的码点。

4.3 直接输入U+2028和U+2029

原本js中有五个字符,不能在字符串中直接使用,只能使用转义形式。

  • U+005C:反斜杠(reverse solidus)

  • U+000D:回车(carriage return)

  • U+2028:行分隔符(line separator)

  • U+2029:段分隔符(paragraph separator)

  • U+000A:换行符(line feed)

    但是在ES2019中,允许字符在直接输入U+2028和U+2029。

4.4 JSON.stringify()的改造

因为JSON数据必须是UTF-8编码,但是先在JSON.stringify()方法可能返回不符合该标准的字符串,为了保证返回的是合法的UTF-8的字符串,ES2019改变了stringify()函数,如果码点处于0xD800~0xDFFF之间,或者不存在配对形式,就会返回转移字符串,留给应用自己决定下一步的处理。

4.5 模板字符串

反引号——`,通过这个符号,就可以不使用\n来换行,就可以直接换行了,也可以直接输入空格。如果不想要换行,可以用trim方法消除。

模板字符串中嵌入变量,需要用${变量名}来实现。大括号内部也可以进行运算,或者引用对象属性,也可以调用函数,如果是个字符串将会原样输出,如果是个对象就会调用对象的toString()转化为字符串输出。

模板字符串也可以写成一个函数作为返回值输出。

4.6 通过模板字符串生成模板(结合正则表达式实现,待完成...
4.7 标签模板

一种函数调用的特殊形式,“标签”指函数,后面的模板就是它的参数。例如alert(反引号)hello(反引号)。

标签模板的一个重要应用就是过滤HTML字符串,防止用户输入恶意内容。还有一个应用就是多语言转换(翻译?),还可以通过标签模板嵌入其他语言。

4.8 模板字符串的限制

因为模板字符串默认将字符串转义,所以无法嵌入其他语言,所以ES2018中放松了对标签模板里的字符串转义的限制,如果遇到不合法的转义就返回undefined。但是这种放松,只有在标签模板解析字符串时才生效。

5.字符串的新增方法

5.1 String.fromCodePoint()(参数是unicode码点)

ES5中提供String.fromCharCode()从Unicode码点返回对应字符,但是不能识别大于0xFFFF的字符,所以ES6中提供了此方法来识别大于0xFFFF的码点。

5.2 String.raw()

将字符串的每一个斜杠都转义,一般用于模板字符串的标签函数。就算字符串中的斜杠已转义为\也会被转义为\\。

5.3 codePointAt()

能够正确处理4个字节储存的字符,返回值是十进制,如果需要十六进制需要toString(16)转换。此仿佛是测试一个字符由两个字节还是四个字节组成的最简单方法(codePointAt(0) > 0xFFFF?)。(UTF-16编码中,BMP内的字符军占用两个字节。)

5.4 normalize()

将字符的不同表示方法统一为同样的形式,称为Unicode正规化。该方法的四个参数:

  • NFC,默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。
  • NFD,表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。
  • NFKC,表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来举例,normalize方法不能识别中文。)
  • NFKD,表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。
5.5 includes()、startsWith()、endsWith()

includes():返回布尔值,表示是否找到了参数字符串。

startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。

endsWith():返回布尔值,表示参数字符串是否在原字符的尾部。

这三个方法都支持第二个参数,前两个表示从第n个位置开始,而endsWith表示前n个字符。

5.6 repeat()

参数表示源字符串重复的次数(n)。参数如果是负数或Infinity,就会报错,如果是0到-1的小数或者NaN,都等同于0。如果参数是字符串,则会先转换成数字。

5.7 padStart()、padEnd()

参数为n和一个字符串,前者表示从头部补全为长度为n的字符串,后者表示从尾部补全。n要小于原字符串的长度,否则返回原字符串。如果没有第二个参数,默认用空格补全。

5.8 trimStart()、trimEnd()

前者表示消除字符串头部的空格,后者表示消除尾部的空格,返回值都是一个新的字符串而不是在原字符串上作更改。

5.9 matchAll()(正则相关,待完成
5.10 replaceAll()

原本的replace()方法只能替换第一个匹配,ES2021引入此方法一次性替换所有匹配。

replaceAll(a,b)将字符串中的所有等于a的字符替换为b。与replace方法和trimStatr()方法一线,返回的是新的字符串。

5.11 at()

返回参数指定位置的字符,支持负索引。at[1]。

6.数值的扩展

6.1 二进制和八进制表示法

二进制新写法:0b/0B;八进制新写法:0o/0O,可以使用Number方法转为十进制。

6.2 数值分隔符

可以在每一、二、三、四位数字间添加一个分隔符(下划线_)

但是,分隔符两边必须都是数字(不包括进制前缀),而且表示指数的e/E前后不能有分隔符。在Number()、parseInt()、parseFloat()函数内的字符串内不支持数值分隔符。

6.3 Number.isFinite(), Number.isNaN()

返回值都是布尔类型,判断数字是否为有限数/NaN。

6.4 Number.parseInt(), Number.parseFloat()

本质就是parseInt()和parseFloat(),主要是为了减少全局性方法,使语言逐步模块化。

6.5 Number.isInteger()

判断一个数值是否为整数。如果数值的精度超过这个限度(53个二进制位,一个隐藏位52个有效位),第54位及后面的位就会被丢弃,这种情况下,Number.isInteger可能会误判。

6.6 Number.EPSILON

js能够表示的最小精度。

image-20211117170320962.png

6.7 安全整数和 Number.isSafeInteger()

js能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。这两个临界值用Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER来表示。该函数来判断一个整数是否在该范围内。

6.8 Math 对象的扩展
6.8.1 Math.trunc():

用于去除一个数的小数部分,返回整数,参数非数值会先用Number转为数值,对于空值和无法截取整数的值会返回NaN。

6.8.2 Math.sign():

判断一个数为正数(+1)、负数(-1)、0(0)、-0(-0)、还是其他值(NaN)。

6.8.3 Math.cbrt():

求一个数的立方根。

6.8.4 Math.clz32():

将参数转为32位无符号整数,然后返回这个32位值里面有多少个前导0。(只考虑整数部分)

6.8.5 Math.imul():

以32位的带符号整数形式返回两个数以32位带符号整数形式相乘的结果。

6.8.6 Math.fround():

返回一个数的32位单精度浮点数形式。

6.8.7 Math.hypot():

返回所有参数的平方和的平方根。

6.8.8 Math.expm1():

返回e^(args)-1。

6.8.9 Math.log1p():

返回1+args的自然对数,如果x<-1,返回NaN。

6.8.10 Math.log10/2():

返回以10/2为底的args的对数。若0则NaN。

image-20211117171815448

6.9 BigInt 数据类型

没有限制位数的整数,为了和Number区分,必须加后缀n。和普通整数不能相等,可以用负号(-)不能用正好,不然与asm.js冲突。

BigInt():将其他类型的值转为BigInt,必须有参数且参数不能为小数或最后一个字符为n的字符串。

BigInt 继承了 Object 对象的两个实例方法。

BigInt.prototype.toString()BigInt.prototype.valueOf()

它还继承了 Number 对象的一个实例方法。

BigInt.prototype.toLocaleString()

此外,还提供了三个静态方法。

BigInt.asUintN(width, BigInt) : 给定的 BigInt 转为 0 到 2width - 1 之间对应的值。 ​ BigInt.asIntN(width, BigInt) :给定的 BigInt 转为 -2width - 1 到 2width - 1 - 1 之间对应的值。 ​ BigInt.parseInt(string[, radix]) :近似于Number.parseInt(),将一个字符串转换成指定进制的BigInt。

可以使用对应的方法将BigInt转为布尔值、数值和字符串类型,转为字符串后n会消失。

7.函数的扩展

7.1 函数参数的默认值

从左向右赋值,使用默认值时,函数不能有同名参数,且默认值应该时函数的尾参数。 1.ES6简介 1.1 ES6与ES5的关系 前者是基于后者改进的一个版本,ES6的第一个版本发布于2015年六月,现在泛指ES5.1后的语言标准。 1.2 ES的历史 ES1.0发布于1997年,后 length属性: 显示没有默认值的参数个数。

作用域: 函数声明初始化时,由默认值的参数会有单独的作用域,初始化结束后该作用域就会消失。

应用: 可以利用参数默认值(throwIfMissing)指定某个参数不能忽略,若忽略则抛出错误。

7.2 rest 参数

(...args),此时args就是一个数组,可以储存多个变量。rest参数只能作为最后一个参数,不然会报错。length属性不包括reset参数。

7.3 严格模式

使用'use strict'进入严格模式,但是如果函数参数用了默认值、结构赋值或者扩展运算符,该函式内部就不能显示设定为严格模式。因为函数执行时,先执行参数再执行函数体,而是否使用严格模式实在函数体中声明的,所以冲突了。但是可以设定全局性的严格模式,或者把函数包放在一个无参数的立即执行函数里。

7.4 name 属性

返回函数的函数名。

7.5 箭头函数

let a =()=>{},参数只有一个时可以省略括号,返回值比较简单时,可以省略return。箭头函数也可以与变量解构结合使用。

注意点:

没有自己的this;不可以当作构造函数;不可以使用arguments对象;不可以使用yield命令。

不适用的情况:

定义对象的方法,且方法内部包括this。

需要动态this时。

7.6 尾调用优化

尾部调用:函数的最后一步时调用另一个函数。返回值为一个函数,就会执行该函数。

不属于尾部调用的情况:

image-20211117175039394.png

尾部调用优化: 只保留内部函数的调用帧(调用记录),如果所有函数都是尾部调用,那么每次执行时,调用帧就只有一项,可以大大节省内存。

尾递归: 尾调用自身就是尾递归。递归消耗内存,容易栈溢出。

严格模式: 尾部调用优化只在严格模式下开启。

这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。

func.arguments:返回调用时函数的参数。 ​ func.caller:返回调用当前函数的那个函数。 ​ 尾调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变 量,所以尾调用模式仅在严格模式下生效。

尾递归优化的实现: 将递归执行转为循环执行,保留了参数。

7.7 函数参数的尾逗号

ES2017允许参数的最后一个参数由尾逗号。

7.8 Function.prototype.toString()

之前的toString会省略注释和空格,ES2019修改后不会省略。

7.9 catch 命令的参数省略

catch的参数可以省略了。

8. 数组的扩展

8.1 扩展运算符
...,类似rest参数的逆运算,将一个数组变为参数序列。可以替代函数的apply方法。

应用:

复制数组、合并数组、与解构赋值结合、将字符串转为真正的数组、实现Iteractor接口的对象、Map和Set结构,

8.2 Array.from()

将类似数组的对象和可遍历的对象(Set、Map)转化为真正的数组。

8.3 Array.of()

将一组值转换为数组。

8.4 实例方法:copyWithin(target, start,end)

在当前数组内部,将指定位置的成员赋值到其他位置(会覆原有成员),然后返回当前数组。后面两个参数可以省略。

8.5 实例方法:find() 和 findIndex()

前者用于找出第一个符合条件的数组成员,返回值为true或者undefined,后者返回值为参数所在位置或-1。

8.6 实例方法:fill()

用一个值填充一个数组,一般用于数组初始化。

8.7 实例方法:entries(),keys() 和 values()

用于比案例数组,返回一个遍历对象,分别遍历键名、键值、键值对。

8.8 实例方法:includes()

判断某个数组是否包含给定的值。

8.9 实例方法:flat(),flatMap()

flat():将嵌套的数组变成一维数组,返回一个新数组,不影响原数据。参数表示“拉平”多少层嵌套。

flatMap():对原数组的每个成员都执行Array.protptype.map()函数,再对返回值的数组执行flat()方法。同上不改变原数组,本方法只能展开一层数组。

flatMap()方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。

8.10 实例方法:at()

支持负索引(从后往前),不仅可以用于数组,也可以用于字符串和类型数组,超出数组的部分显示undefined。

8.11 数组的空位

Array()构造函数返回的数组都是空位,ES5大多情况会忽略空位,但是ES6将空位转为undefined。扩展运算符也会将空位转为undefined。

copyWithin()连空位一起拷贝,file()将空位是为正常的数组位置,for...of循环也会遍历空位。entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。

8.12 Array.prototype.sort() 的排序稳定性

排序稳定性(stable sorting)是排序算法的重要属性,指的是排序关键字相同的项目,排序前后的顺序不变。

ES2019规定, Array.prototype.sort() 的默认排序算法必须稳定,之前的一些的排序算法之中,插入排序、合并排序、冒泡排序等都是稳定的,堆排序、快速排序等是不稳定的。