说起来最近一直在填 ES6 的基础坑。
《ES6 标准入门》真好用。
不过其实虽然书名是 ES6,其实也包含了一些 ES7 和 ES8 的内容。(因为第三版是 2017.9 修订的
参考链接/书籍
《ES6 标准入门(第三版)》
ES6、ES7、ES8、ES9、ES10 新特性一览
ES10 都出了,还没弄明白 ES6?
MDN
ES6
ES6 因为是夸了好几年的大版本,所以新特性相对特别多。
- let/const
- 结构赋值
- class
- 模块化(import / export)
- 箭头函数
- 模版字符串(就是`${}`)
- 拓展运算符
- Promise
- Generator
- Symbol
- Set/Map
- Proxy
- Reflect(…突然看到了完全不知道的 ES6 特性)
- 函数默认参数
- 正则拓展
- 对象拓展
- Array 拓展
- String 拓展
- Number 拓展
- Math 拓展
有些内容已经熟的不能再煮了,有些要拿出来说又是一篇满满当当的文章。。。
所以就 8 说了,来看看最后几个拓展
Array 拓展
先说说 Array 拓展,因为两次面试都有提到 ES6 的数组拓展,但是因为用来用去也就那些个函数,没有去研究过 Array 的版本迭代,所以先来康康。
Array 的 ES6 拓展有:(判断依据是 MDN,虽然部分函数在 ES7 及之后的版本都有相关规范,但是毕竟是出生在 ES6 的。
- 拓展运算符
- Array.from()
- Array.of()
- Array.prototype.copyWithin()
- Array.prototype.entries()
- Array.prototype.keys()
- Array.prototype.values()
- Array.prototype.fill()
- Array.prototype.find()
- Array.prototype.findIndex()
Array.from() & Array.of()
两个的返回值都是一个新数组
参数
Array.from(arrayLike[, mapFn[, thisArg]])
arrayLike 就是想要转换成数组的伪数组对象或可迭代对象
mapFn (可选)新的数组的每个元素都会执行这个函数
thisArg (可选)执行 mapFn 函数时的 this 对象
Array.of([element0[, element1[, …[, elementN]]]])
参数就是生成的数组的元素。没有参数返回空数组。
返回值
都是返回一个新数组。
需要注意的是 Array.from() 是浅拷贝
作用
Array.from() 是把第一个参数(数组、伪数组、可迭代对象)转换为一个新的数组。如果有第二个参数,则新的数组的每一项都会执行这个函数。如果有第三个参数,则第二个函数的 this 为第三个参数。
Array.of() 是把所有的参数组成一个数组。
拓展运算符
就是用于把数组展开成参数序列。
若果接的是空数组,则没有任何效果
1 | let a = [...[], 1]; |
还可以用来处理字符串
1 | let demo = [..."demo"]; |
还可以用来解析遍历器对象
1 | const go = function*() { |
其他的用法就常规的拆分(当然不是 split 那种拆分)一下数组,帮助结合一下数组什么的。
Array.prototype.copyWithin()
Array.prototype.copyWithin(target[, start=0[, end=this.length]])
实现的功能是在数组内指定位置(部分)的成员复制到其他位置。
参数
target 从这个位置开始替换
start 从整个位置开始截取需要用于复制的成员
end 默认为数组长度,不包括 end 这个位置的元素
注意:三个参数都是以 0 为基底的索引
1 | let demo = [1, 2, 3, 4, 5, 6].copyWithin(1, 3, 5); |
上面的例子中,截取索引为 3 开始,索引为 5 截止的部分,从索引为 1 的位置开始进行替换,替换只进行一次。
1 | let demo = [1, 2, 3, 4, 5, 6, 7, 8].copyWithin(1, 3, -1); |
上面的例子中使用了负数
如果 start 是个负数, 则开始索引会被自动计算成为 length+start, 其中 length 是 this 对象的 length 属性值。如果 end 是个负数, 则结束索引会被自动计算成为 length+end。 -- MDN
截取第四位(下标为 3)到倒数第二(下标为 6)这部分成员,即 4,5,6,7,从数组第二位(下标为 1)开始替换,就是把原来的 2,3,4,5 替换成了 4,5,6,7。
entries() & keys() & values()
entries() 某种程度上算是 keys() 和 values() 的合集。
三个函数都是返回遍历器对象
keys() 返回数组下标
values() 返回数组元素
entries() 返回下标和元素
1 | let demo = ["a", "b", "c"]; |
需要注意的是使用 entries() 返回的结果是下标在前,元素在后。
因为是遍历器对象,如果不用 for of 的话,可以使用 next()手动调用。
1 | let demo = ["a", "b", "c"].entries(); |
Array.prototype.fill()
Array.prototype.fill(value[, start = 0 [, end = this.length]])
使用指定值填充数组,会改变原数组
start 和 end 参数与 copyWithin 一样,值为负数时,代表的下标为 length+end/start
1 | let demo = [1, 2, 3]; |
find() & findIndex()
Array.prototype.find(callback[, thisArg) 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
Array.prototype.findIndex(callback[, thisArg) 方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。
和 indexOf() 有点像。
不过 find() 和 findIndex() 的参数都是一个回调函数以及一个可选的回调函数的 this 对象。
回调函数的参数为元素本身、元素下标以及数组本身。
String 拓展
- Unicode 表示法
- 模版字符串
- String.fromCodePoint()
- String.prototype.codePointAt()
- String.prototype.{endsWith(), includes(), startWith()}
- String.prototype.normalize()
- String.prototype.repeat()
Unicode 表示法
JavaScript 允许使用'\uxxxx'表示 Unicode 字符,但是仅限于 0000-FFFF,超出范围就需要用两字节表示。
1 | console.log("\uD842\uDFB7"); // "𠮷" |
ES6 开始允许在 \u 之后使用大括号,就能正确解读
1 | console.log("\u{20BB7}"); // "𠮷" |
模版字符串
`${}`
允许字符串多行。
${}之内可以是变量、函数调用、JavaScript 表达式。
花括号内如果不是字符串,则会按照一般规则转为字符串。比如为对象,则会调用 toString()。
内部变量为声明会报错。
String.fromCodePoint()
返回使用指定的代码点序列创建的字符串。
String.fromCodePoint(num1[, …[, numN]])
参数
numN Unicode 编码位置。
返回值
使用指定的 Unicode 编码位置创建的字符串。
若传入的 Unicode 编码无效则报 RangeError 错误。
1 | String.fromCodePoint(42); // "*" |
String.prototype.codePointAt()
返回 一个 Unicode 编码点值的非负整数。
str.codePointAt(pos)
参数
pos 字符串中需要转码的元素的位置。
返回值
在字符串中的给定索引的编码单元体现的数字,如果在索引处没找到元素则返回 undefined 。
1 | "ABC".codePointAt(1); // 66 |
String.prototype.{endsWith(), includes(), startWith()}
str.includes(searchString[, position])
用于判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false。
大小写敏感。
参数
searchString 要在此字符串中搜索的字符串。
position 可选。默认为 0。用于表示从原字符串的哪个索引位置开始搜寻子字符串。
返回值
子字符串存在于原字符串则返回 true,不存在则返回 false。
1 | "Blue Whale".includes("blue"); // false |
str.endsWith(searchString[, length])
str.startsWith(searchString[, position])
endsWith()/startsWith()用来判断当前字符串是否是以另外一个给定的子字符串"结尾"/"开头"的,根据判断结果返回 true 或 false。
参数
searchString 要在此字符串中搜索的字符串。
length 可选。作为 str 的长度。默认值为 str.length。
position 可选。默认为 0。在 str 中搜索 searchString 的开始位置,也就是真正的字符串开头处。
返回值
如果传入的子字符串在搜索字符串的末尾/开头则返回 true;否则将返回 false。
String.prototype.normalize()
str.normalize([form]);
会按照指定的一种 Unicode 正规形式将当前字符串正规化.
参数
form 默认为"NFC"。四种 Unicode 正规形式 “NFC”, “NFD”, “NFKC”, 以及 “NFKD” 其中的一个。
返回值
含有给定字符串的 Unicode 规范化表单的字符串。
如果给 form 传入了非法的参数值, 则会抛出 RangeError 异常.
String.prototype.repeat()
str.repeat(count);
构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。
参数
count 取值范围[0,+∞)。默认为 0。表示在新构造的字符串中重复了多少遍原字符串。
返回值
包含指定字符串的指定数量副本的新字符串。
若 count<0 则会抛出 RangeError 异常。
1 | "abc".repeat(-1); // RangeError: repeat count must be positive and less than inifinity |
Number 拓展
- 二/八进制表示法
- Number.{inNaN(), isFinite(), isInteger(), isSafeInteger()}
- Number.{parseInt(), parseFloat()}
- Number.EPSLION
- Number.{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER}
二/八进制表示法
使用 0b/0B 表示二进制。
使用 0o/0O 表示八进制。
1 | 0b111110111 === 503; // true |
Number.{isNaN(), isFinite(), isInteger(), isSafeInteger()}
ES6 新增的四个用于判断的方法。四个方法都不会将参数进行强制转换。
Number.isNaN(num)用来确定 num 是否为 NaN 以及其类型是否是 Number(全局的 isNaN()会把参数强制转化成数值)
Number.isFinite(num)用来检测 num 是否是一个有穷数以及其类型是否是 Number(全局的 isFinite()会把参数强制转化成数值)
Number.isInteger(num) 用来判 num 是否为整数以及其类型是否是 Number。NaN 和正负 Infinity 不是整数。
Number.isSafeInteger(num)用来判断 num 是否是一个安全整数以及其类型是否是 Number。安全整数范围为 -(253 - 1)到 253 - 1 之间的整数,包含 -(253 - 1)和 253 - 1。
1 | Number.isNaN(NaN); // true |
Number.EPSLION
就是 ε。
Number.EPSILON 属性表示 1 与 Number 可表示的大于 1 的最小的浮点数之间的差值。
Number.EPSLION 属性值接近于 2.2204460492503130808472633361816E-16,或者 2-52。
Number.{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER}
就是最大最小安全数。
1 | Number.MAX_SAFE_INTEGER === 2 ** 53 - 1; // true |
Math 拓展
- Math.{acosh(), asinh(), atanh()}
- Math.{cosh(), sinh(), tanh()}
- Math.cbrt()
- Math.clz32()
- Math.expm1()
- Math.fround()
- Math.hypot()
- Math.imul()
- Math.{log10(), Math.log1p(), log2()}
- Math.trunc()
Math.{acosh(), asinh(), atanh(), cosh(), sinh(), tanh()}
… 涉及到了我的知识盲区。
Math.cbrt()
cbrt 是 "cube root" 的缩写, 意思是立方根。
Math.sqrt(x) 是求 x 的平方根,那么 Math.cbrt(x)就是求 x 的立方根。
如果 x 不是数字类型,则会被自动类型转换成 number 类型。
Math.clz32()
Math.clz32() 函数返回一个数字在转换成 32 无符号整形数字的二进制形式后, 开头的 0 的个数, 比如 1000000 转换成 32 位无符号整形数字的二进制形式后是 00000000000011110100001001000000, 开头的 0 的个数是 12 个, 则 Math.clz32(1000000) 返回 12.
clz32 是 "CountLeadingZeroes32" 的缩写。
如果 x 不是数字类型, 则它首先会被转换成数字类型, 然后再转成 32 位无符号整形数字.
Math.expm1()
返回 Ex - 1, 其中 x 是该函数的参数, E 是自然对数的底数 2.718281828459045.
expm1 是 "exponent minus 1" 的缩写.
如果 x 不是数字类型,则会被自动类型转换成 number 类型。
Math.fround()
将任意的数字转换为离它最近的单精度浮点数形式的数字。
Math.fround(doubleFloat)
参数
doubleFloat Number。若参数为非数字类型,则会被转投成数字。无法转换时,设置成 NaN。
返回值
指定数字最接近的 32 位单精度浮点数表示。
JavaScript 内部使用 64 位的双浮点数字,支持很高的精度。但是,有时你需要用 32 位浮点数字,比如你从一个 Float32Array 读取值时。这时会产生混乱:检查一个 64 位浮点数和一个 32 位浮点数是否相等会失败,即使二个数字几乎一模一样。
要解决这个问题,可以使用 Math.fround() 来将 64 位的浮点数转换为 32 位浮点数。在内部,JavaScript 继续把这个数字作为 64 位浮点数看待,仅仅是在尾数部分的第 23 位执行了“舍入到偶数”的操作,并将后续的尾数位设置为 0。如果数字超出 32 位浮点数的范围,则返回 Infinity 或 -Infinity。
因为 fround() 是 Math 的静态方法,你必须通过 Math.fround() 来使用,而不是调用你创建的 Math 对象的一个实例方法(Math 不是一个构造函数)
在某些精度不高的场合下,可以通过将二个浮点数转换成 32 位浮点数进行比较,以解决 64 位浮点数比较结果不正确的问题:
1 | 0.1 + 0.2 == 0.3; //false |
Math.hypot()
返回它的所有参数的平方和的平方根
Math.hypot([value1[,value2, …]])
参数
valueN 任意多个数字
返回值
将所提供的参数求平方和后开平方根的结果。如果有参数不能转换为数字,则返回 NaN。
Math.imul()
返回两个参数的类 C 的 32 位整数乘法运算的运算结果.
Math.imul(a, b)
参数
a 乘数
b 被乘数
Math.imul 可以进行快速的,类 C 语义的 32 位整数乘法.该特性对于一些项目比如 Emscripten 很有用.
Math.{log10(), Math.log1p(), log2()}
对数操作函数。
Math.log10() 函数返回一个数字以 10 为底的对数.
Math.log2() 函数返回一个数字以 2 为底的对数.
Math.log1p() 函数返回一个数字加 1 后的自然对数 (底为 E), 即 ln(x+1)。
Math.trunc()
会将数字的小数部分去掉,只保留整数部分。
不像 Math 的其他三个方法: Math.floor()、Math.ceil()、Math.round() ,Math.trunc() 的执行逻辑很简单,仅仅是删除掉数字的小数部分和小数点,不管参数是正数还是负数。
传入该方法的参数会被隐式转换成数字类型。
1 | Math.trunc(13.37); // 13 |
ES7
只有两个新特性。
- Array.prototype.includes()
- 指数操作符。
Array.prototype.includes()
arr.includes(valueToFind[, fromIndex])
也是用于查找数组中是否存在某个值的函数。
不可以用于对象数组
查找字符串、字符时,大小写敏感
参数
valueToFind 需要查找的参数
fromIndex 可选,默认为 0。从 fromIndex 为下标开始查找。大于数组长度时,不进行搜索直接返回 false。负数时,从 arr.length+fromIndex 开始找,若 arr.length+fromIndex < 0 则整个数组都会被搜索。
返回值
true/fasle,表示数组中存在/不存在查找的内容。
指数操作符
也称求幂运算符。
两个乘号(**)作为指数操作符。
1 | 2 ** 3; // 8 |
ES8
ES8 新特性也挺多的。
- async/await
- 字符串填充(padStart/padEnd)
- Object.{values(), entries()}
- Object.getOwnPropertyDescriptors()
- 尾部逗号
- Atomics & SharedArrayBuffer
async/await
String.prototype.{padStart(), padEnd()}
str.padStart(targetLength [, padString])
参数
targetLength 填充的目标长度。若小于字符串长度则返回字符串本身(并不会缩减)
padString 填充字符串。默认为""(u+0020)。若填充字符串大于可填充位置,则只填充左侧部分。
返回值
被填充好的新字符串
1 | "abc".padStart(10); // " abc" |
str.padEnd(targetLength [, padString])
和 padStart()一致,区别在于向后填充。
1 | "abc".padEnd(10); // "abc " |
Object.{values(), entries()}
可以参考下 Array.prototype.keys/values()/entries()。
Object.keys(obj) 返回由 obj 的自身的可枚举属性组成的数组
Object.values(obj) 返回由 obj 的自身的可枚举属性值组成的数组
Object.entries(obj) 返回由 obj 的自身的可枚举属性的键值对组成的数组
三个函数返回的数组的排列与使用 for…in 循环遍历该对象时返回的顺序一致
区别在于 for…in 还会拿到原型链上的属性。
Object.getOwnPropertyDescriptors()
用来获取一个对象的所有自身属性的描述符。
关于描述符 -> 讲清楚之 javascript 对象属性描述符
逗号结尾
允许函数调用、定义时,参数以逗号结尾
1 | function es8(var1, var2, var3,) {} |
Atomics & SharedArrayBuffer
Atomics 是用于对 ShardArrayBuffer 对象进行原子操作的东西。
SharedArrayBuffer
Atomics
ES9
- 一些正则拓展
- 对象的拓展运算符 (Rest/Spread)
- 异步迭代器
- Promise.prototype.finally()
对象的 Rest/Spread
就是说对象里也可以使用拓展运算符 … 了。
1 | let demo = { |
可以使用拓展运算符实现对象拷贝,需要注意的是和 Objec.assign() 一样是浅拷贝
异步迭代器
使 await 可以和 for of 一起使用。
1 | async function process(arr){ |
for of 仅适用于同步遍历器,而 for await of 同步遍历器和异步遍历器都适用。
Promise.prototype.finally()
在《ES6 标准入门》里,提到了这个方法,但是是阮一峰老师自己写的一个 polyfill。同时提出的 done()函数并没有进入(或者说出现)标准。
在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。
但是 MDN 里显示这个方法还处于草案阶段。
ES9 的正则拓展
- 允许给捕获组命名
- 反向(后行)断言/反向(后行)否定断言
- dotAll (s 修饰符)
也没什么好说的吧。。。
关于 JavaScript 正则
文章里没有提到 dotAll,其实就是新增了一个 s 修饰符。
原先的 js 正则里,点( . )匹配除回车外的任何单字符,但是使用 s 修饰符会这种行为,允许行终止符的出现
1 | /hello.world/.test("hello\nworld"); // false |
ES10
- Array.prototype.{flat(), flatMap()}
- Object.fromEntries()
- String.prototype.{trimStart() ,trimEnd()}
- String.prototype.matchAll()
- Symbol.prototype.description
- BigInt
- 允许省略 try/catch 语句中的 catch 的参数
- 允许字符串字面量中允许出现 U+2028 和 U+2029
- 要求 Array.prototype.sort 的排序算法必须是稳定的(即不改变相同元素的顺序)
- 要求 JSON.stringify() 返回 UTF-8
- 要求 Function.prototype.toString 返回 function 源码文本,或标准占位符
flat() && flatMap()
Array.prototype.flat([depth])
用于扁平化数组。
参数
depth 可选,要提取嵌套数组的结构深度,默认值为 1。
返回值
新数组
1 | let arr = [1, 2, [3, 4, [5, 6]]]; |
Array.prototype.flatMap(function callback(currentValue[, index[, array]]) {}[, thisArg])
用于使用映射函数映射每个元素,然后将结果压缩成一个新数组。
它与 map 和 深度值 1 的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
参数
callback 函数。三个参数同 find(),也是正在进行映射的数组元素、元素下标以及整个数组。函数的返回值是新数组的元素。
thisArg 可选,执行 callback 时的 this。
返回值
新数组,每个元素都是回调函数的结果,并且结构深度 depth 值为 1。
Object.fromEntries()
与 Obeject.entries() 作用相反。把值键对转换成对象。
参数
iterable 可迭代对象。
1 | const map = new Map([["foo", "bar"], ["baz", 42]]); |
String.prototype.{trimStart() ,trimEnd()}
trimStart() 方法从字符串的开头删除空格。trimLeft()是此方法的别名。
trimEnd() 方法从字符串的开头删除空格。trimRight()是此方法的别名。
返回值
删除了原字符串左/右侧空格的新字符串(不会改变原字符串)
1 | let demo = " abc "; |
String.prototype.matchAll()
方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。
Symbol.prototype.description
1 | const sym = Symbol("The description"); |