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对象。
而在node环境下,顶层对象是global对象。
在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。
嵌套结构的解构。
如果下面的赋值是let {p:[x,{y}]} = obj,此时的p就是一个模式而不是变量。
对象的解构赋值可以取到继承的属性。
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 为什么要解构赋值
- 变换变量的值。(swap)
- 从函数返回多个值。
- 函数参数的定义。
- 提取JSON数据。
- 函数参数的默认值。
- 遍历Map结构。(let [key] of map、let [,value] of map)
- 输入模块的指定方法。(require、import的应用。)
4.字符串的扩展
4.1 字符串的Unicode表示法
允许用\uxxxx(\u0000~\uFFFF)来表示一个字符,原本超过这个范围就必须用两个双字节的形式表示。如果超过0xFFFF,js就会将其拆分,例如\u20BB7就会拆成\u20BB+7,\U20BB是一个不可打印的字符,此时就会显示一个空格后面跟一个7。
但在ES6中,即使超过这个范围,将码点放入大括号,就能正确解读该字符。
所以ES6中,表示一个字符就有六种方法。
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能够表示的最小精度。
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。
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 尾调用优化
尾部调用:函数的最后一步时调用另一个函数。返回值为一个函数,就会执行该函数。
不属于尾部调用的情况:
尾部调用优化: 只保留内部函数的调用帧(调用记录),如果所有函数都是尾部调用,那么每次执行时,调用帧就只有一项,可以大大节省内存。
尾递归: 尾调用自身就是尾递归。递归消耗内存,容易栈溢出。
严格模式: 尾部调用优化只在严格模式下开启。
这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。
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() 的默认排序算法必须稳定,之前的一些的排序算法之中,插入排序、合并排序、冒泡排序等都是稳定的,堆排序、快速排序等是不稳定的。