从 ECMAScript 2016(ES7)已经开始,JavaScript 的版预览显得愈来愈频密,每月正式发布三个新版。因此,相同版有什么样值得称赞高度关注的优点?又有什么样的改良?好在每天版预览的文本并不多,责任编辑将剖析相同版的优点,尽量和旧科学知识密切相关,帮你快速认知那些优点。
ES7 新优点
1.Array.prototype.includes() 方式
在 ES6 中他们有 String.prototype.includes() 能查阅取值数组与否包涵三个字符串,而在 ES7 中,他们在数组中也能用 Array.prototype.includes 方式来推论三个数组与否包涵三个选定的值,依照情形,假如包涵则回到 true,不然回到 false。
const arr = [1, 3, 5, 2, 8, NaN, -0]
arr.includes(1) // true
arr.includes(1, 2) // false 该方式的第三个模块则表示搜寻的初始边线,预设为 0
arr.includes(1) // false
arr.includes(NaN) // true
arr.includes(+0) // true
在 ES7 以后想推论数组中与否包涵三个原素,有如下表所示三种方式, 但都比不上 includes 来得简单:
indexOf()indexOf() 方式回到在数组中能找寻三个取值原素的第三个检索,假如不存有,则回到 -1。
if (arr.indexOf(el) !== -1) {
// …
}
但是此种方式有三个优点,其一比不上语法化,要先找寻初始值的第三个再次出现边线,因此要去较为与否不等同于 -1,抒发出来比不上简单。并有,它外部采用严苛成正比操作符(===)展开推论,这会引致对 NaN 的错判。
[NaN].indexOf(NaN)// -1
find() 和 findIndex()数组示例的 find 方式,用作找寻第三个具备条件的数组核心成员。除此之外,这三个方式都能发现 NaN,填补了数组的 indexOf 方式的严重不足。
[1, 4, -5, 10].find((n) => n < 0) // -5
[1, 5, 10, 15].findIndex(function(value) {
return value > 9;
}) // 2
[NaN].findIndex(y => Object.is(NaN, y)) // 0
Array.prototype.includes() 的支持情形:
2. 求幂操作符 **在 ES7 中引入了指数操作符,具有与 Math.pow() 等效的计算结果
console.log(2**10);// 输出 1024zhicc
console.log(Math.pow(2, 10)) // 输出 1024
求幂操作符的支持情形:
ES8 新优点
1.Async/Await
他们都知道采用 Promise 能很好地解决回调地狱的问题,但假如处理流程较为复杂的话,因此整段代码将充斥着 then,语法化不明显,代码不能很好地则表示执行流程,那有没有比 Promise 更优雅的异步方式呢?
假如有这样三个采用场景:需要先请求 a 链接,等回到信息之后,再请求 b 链接的除此之外三个资源。下面代码展示的是采用 fetch 来实现这样的需求,fetch 被定义在 window 对象中,它回到的是三个 Promise 对象。
fetch(https://blog.csdn.net/)
.then(response => {
console.log(response)
return fetch(https://juejin.im/)
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
虽然上述代码能实现这个需求,但语法化不明显,代码不能很好地则表示执行流程。基于这个原因,ES8 引入了 async/await,这是 JavaScript 异步编程的三个重大改良,提供了在不阻塞主线程的情况下采用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰。
async function foo () {
try {
let response1 = await fetch(https://blog.csdn.net/)
console.log(response1)
let response2 = await fetch(https://juejin.im/)
console.log(response2)
} catch (err) {
console.error(err)
}
}
foo()
通过上面代码,你会辨认出整个异步处理的逻辑都是采用同步代码的方式来实现的,而且还支持 try catch 来捕获异常,这感觉就在写同步代码,因此是非常符合人的线性思维的。需要强调的是,await 不能脱离 async 单独采用,await 后面一定是 Promise 对象,假如不是会自动包装成 Promise 对象。
依照 MDN 定义,async 是三个通过异步执行并隐式回到 Promise 作为结果的函数。
async function foo () {
return 浪里行舟
}
foo().then(val => {
console.log(val) // 浪里行舟
})
上述代码,他们能看到调用 async 声明的 foo 函数回到了三个 Promise 对象,等价于下面代码:
async function foo () {
return Promise.resolve(浪里行舟)
}
foo().then(val => {
console.log(val) // 浪里行舟
})
Async/Await 的支持情形:
2.Object.values(),Object.entries()
ES5 引入了 Object.keys 方式,回到三个数组,核心成员是模块对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。ES8 引入了跟 Object.keys 配套的 Object.values 和 Object.entries,作为遍历三个对象的补充手段,供 for…of 循环采用。
Object.values 方式回到三个数组,核心成员是模块对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
const obj = { foo: bar, baz: 42 };
Object.values(obj) // [“bar”, 42]
const obj = { 100: a, 2: b, 7: c };
Object.values(obj) // [“b”, “c”, “a”]
需要注意的是,假如属性名为数值的属性,是按照数值大小,从小到大遍历的,因此回到的顺序是 b、c、a。
Object.entries() 方式回到三个数组,核心成员是模块对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。这个优点他们后面介绍 ES10 的 Object.fromEntries() 还会再提到。
const obj = { foo: bar, baz: 42 };
Object.entries(obj) // [ [“foo”, “bar”], [“baz”, 42] ]
const obj = { 10: xxx, 1: yyy, 3: zzz };
Object.entries(obj); // [[1, yyy], [3, zzz], [10: xxx]]
Object.values() 与 Object.entries() 兼容性一致,下面以 Object.values() 为例:
3.String padding
在 ES8 中 String 新增了三个示例函数 String.prototype.padStart 和 String.prototype.padEnd,允许将空数组或其他数组添加到原始数组的开头或结尾。他们先看下采用语法:
String.padStart(targetLength,[padString])
targetLength(必填): 当前数组需要填充到的目标长度。假如这个数值小于当前数组的长度,则回到当前数组本身。padString(可选): 填充数组。假如数组太长,使填充后的数组长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此模块的缺省值为 ” “。x.padStart(4, ab) // abax
x.padEnd(5, ab) // xabab
有时候他们处理日期、金额的时候经常要格式化,这个优点就派上用场:
12.padStart(10, YYYY-MM-DD) // “YYYY-MM-12”
09-12.padStart(10, YYYY-MM-DD) // “YYYY-09-12”
String padding 的支持情形:
4.Object.getOwnPropertyDescriptors()
ES5 的 Object.getOwnPropertyDescriptor() 方式会回到某个对象属性的描述对象(descriptor)。ES8 引入了 Object.getOwnPropertyDescriptors() 方式,回到选定对象所有自身属性(非继承属性)的描述对象。
const obj = {
name: 浪里行舟,
get bar () {
return abc
}
}
console.log(Object.getOwnPropertyDescriptors(obj))
得到如下表所示结果:
该方式的引入目的,主要是为了解决 Object.assign() 无法正确拷贝 get 属性和 set 属性的问题。他们来看个例子:
const source = {
set foo (value) {
console.log(value)
},
get bar () {
return 浪里行舟
}
}
const target1 = {}
Object.assign(target1, source)
console.log(Object.getOwnPropertyDescriptor(target1, foo))
回到如下表所示结果:
上面代码中,source 对象的 foo 属性的值是三个赋值函数,Object.assign 方式将这个属性拷贝给 target1 对象,结果该属性的值变成了 undefined。这是因为 Object.assign 方式总是拷贝三个属性的值,而不会拷贝它背后的赋值方式或取值方式。
这时 Object.getOwnPropertyDescriptors() 方式配合 Object.defineProperties() 方式,就能实现正确拷贝。
const source = {
set foo (value) {
console.log(value)
},
get bar () {
return 浪里行舟
}
}
const target2 = {}
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source))
console.log(Object.getOwnPropertyDescriptor(target2, foo))
回到如下表所示结果:
Object.getOwnPropertyDescriptors() 的支持情形:
ES9 新优点
1.for await of
for of方式能够遍历具有 Symbol.iterator 接口的同步迭代器数据,但是不能遍历异步迭代器。ES9 新增的 for await of 能用来遍历具有 Symbol.asyncIterator 方式的数据结构,也就是异步迭代器,且会等待前三个核心成员的状态改变后才会遍历到下三个核心成员,相当于 async 函数外部的 await。现在他们有三个异步任务,想要实现依次输出结果,该如何实现呢?
// for of 遍历
function Gen (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(time)
}, time)
})
}
async function test () {
let arr = [Gen(2000), Gen(100), Gen(3000)]
for (let item of arr) {
console.log(Date.now(), item.then(console.log))
}
}
test()
得到如下表所示结果:
上述代码证实了 for of 方式不能遍历异步迭代器,得到的结果并不是他们所期待的,于是 for await of 就粉墨登场啦!
function Gen (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(time)
}, time)
})
}
async function test () {
let arr = [Gen(2000), Gen(100), Gen(3000)]
for await (let item of arr) {
console.log(Date.now(), item)
}
}
test()
// 1575536194608 2000
// 1575536194608 100
// 1575536195608 3000
采用 for await of 遍历时,会等待前一个 Promise 对象的状态改变后,再遍历到下三个核心成员。
异步迭代器的支持情形:
2.Object Rest SpreadES6 中添加的最意思的优点之其一 spread 操作符。你不仅能用它替换 cancat() 和 slice() 方式,使数组的操作 (复制、合并) 更加简单,还能在数组必须以拆解的方式作为函数模块的情形下,spread 操作符也很实用。
const arr1 = [10, 20, 30];
const copy = […arr1]; // 复制
console.log(copy); // [10, 20, 30]
const arr2 = [40, 50];
const merge = […arr1, …arr2]; // 合并
console.log(merge); // [10, 20, 30, 40, 50]
console.log(Math.max(…arr)); // 30 拆解
ES9 通过向对象文本添加扩展属性进一步扩展了此种语法。他能将一个对象的属性拷贝到另三个对象上,参考以下情形:
const input = {
a: 1,
b: 2,
c: 1
}
const output = {
…input,
c: 3
}
console.log(output) // {a: 1, b: 2, c: 3}
上面代码能把 input 对象的数据都添加到 output 对象中,需要注意的是,假如存有相同的属性名,只有最后三个会生效。
const input = {
a: 1,
b: 2
}
const output = {
…input,
c: 3
}
input.a=浪里行舟
console.log(input,output) // {a: “浪里行舟”, b: 2} {a: 1, b: 2, c: 3}
上面例子中,修改 input 对象中的值,output 并没有改变,说明扩展操作符拷贝三个对象(类似这样 obj2 = {…obj1}),实现只是三个对象的浅拷贝。值得称赞注意的是,假如属性的值是三个对象的话,该对象的引用会被拷贝:
const obj = {x: {y: 10}};
const copy1 = {…obj};
const copy2 = {…obj};
obj.x.y=浪里行舟
console.log(copy1,copy2) // x: {y: “浪里行舟”} x: {y: “浪里行舟”}
console.log(copy1.x === copy2.x); // → true
copy1.x 和 copy2.x 指向同三个对象的引用,因此他们严苛成正比。
他们再来看下 Object rest 的示例:
const input = {
a: 1,
b: 2,
c: 3
}
let { a, …rest } = input
console.log(a, rest) // 1 {b: 2, c: 3}
当对象 key-value 不确定的时候,把必选的 key 赋值给变量,用三个变量收敛其他可选的 key 数据,这在以后是做不到的。注意,rest 属性必须始终再次出现在对象的末尾,不然将抛出错误。
Rest 与 Spread 兼容性一致,下列以 spread 为例:
3.Promise.prototype.finally()Promise.prototype.finally() 方式回到三个 Promise,在 promise 执行结束时,无论结果是 fulfilled 或者是 rejected,在执行 then() 和 catch() 后,都会执行 finally 选定的回调函数。
fetch(https://www.google.com)
.then((response) => {
console.log(response.status);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
document.querySelector(#spinner).style.display = none;
});
无论操作与否成功,当您需要在操作完成后展开一些清理时,finally() 方式就派上用场了。这为选定执行完 promise 后,无论结果是 fulfilled 还是 rejected 都需要执行的代码提供了一种方式,避免同样的语句需要在 then() 和 catch() 中各写一次的情形。
Promise.prototype.finally() 的支持情形:
4. 新的正则抒发式优点ES9 为正则表达式添加了四个新优点,进一步提高了 JavaScript 的数组处理能力。那些特点如下表所示:
s (dotAll) 标志命名捕获组Lookbehind 后行断言Unicode 属性转义s(dotAll)flag 正则抒发式中,点(.)是三个特殊字符串,代表任意的单个字符串,但是有三个例外。三个是四个字节的 UTF-16 字符串,这个能用 u 修饰符解决;另三个是行终止符, 如换行符 (\n) 或回车符 (\r), 这个能通过 ES9 的 s(dotAll)flag,在原正则抒发式基础上添加 s 则表示:
console.log(/foo.bar/.test(foo\nbar)) // false
console.log(/foo.bar/s.test(foo\nbar)) // true
那如何推论当前正则与否采用了 dotAll 模式呢?
const re = /foo.bar/s // Or, `const re = new RegExp(foo.bar, s);`.
console.log(re.test(foo\nbar)) // true
console.log(re.dotAll) // true
console.log(re.flags) // s
命名捕获组
在一些正则抒发式模式中,采用数字展开匹配可能会令人混淆。例如,采用正则抒发式 /(\d{4})-(\d{2})-(\d{2})/ 来匹配日期。因为美式英语中的日期则表示法和英式英语中的日期则表示法相同,因此很难区分哪一组则表示日期,哪一组则表示月份:
const re = /(\d{4})-(\d{2})-(\d{2})/;
const match= re.exec(2019-01-01);
console.log(match[0]); // → 2019-01-01
console.log(match[1]); // → 2019
console.log(match[2]); // → 01
console.log(match[3]); // → 01
ES9 引入了命名捕获组,允许为每三个组匹配选定三个名字,既便于阅读代码,又便于引用。
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = re.exec(2019-01-01);
console.log(match.groups); // → {year: “2019”, month: “01”, day: “01”}
console.log(match.groups.year); // → 2019
console.log(match.groups.month); // → 01
console.log(match.groups.day); // → 01
上面代码中,“命名捕获组”在圆括号外部,模式的头部添加“问号 + 尖括号 + 组名”(?),然后就能在 exec 方式回到结果的 groups 属性上引用该组名。
命名捕获组也能采用在 replace() 方式中,例如将日期转换为美国的 MM-DD-YYYY 格式:
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const usDate = 2018-04-30.replace(re, $<month>-$<day>-$<year>)
console.log(usDate) // 04-30-2018
Lookbehind 后行断言
JavaScript 语言的正则抒发式,只支持先行断言,不支持后行断言,先行断言他们能简单认知为”先遇到三个条件,再推论后面与否满足”,如下表所示面例子:
let test = hello world
console.log(test.match(/hello(?=\sworld)/))
// [“hello”, index: 0, input: “hello world”, groups: undefined]
但有时他们想推论前面是 world 的 hello,这个代码是实现不了的。在 ES9 就支持这个后行断言了:
let test = world hello
console.log(test.match(/(?<=world\s)hello/))
// [“hello”, index: 6, input: “world hello”, groups: undefined]
(?<…) 是后行断言的符号,(?..) 是先行断言的符号,然后结合 =(等同于)、!(不等)、\1(捕获匹配)。
Unicode 属性转义 ES2018 引入了一种新的类的写法\p{…}和\P{…},允许正则抒发式匹配符合 Unicode 某种属性的所有字符串。比如你能采用\p{Number}来匹配所有的 Unicode 数字,例如,假设你想匹配的 Unicode 字符串㉛数组:
const str = ㉛;
console.log(/\d/u.test(str)); // → false
console.log(/\p{Number}/u.test(str)); // → true
同样的,你能采用\p{Alphabetic}来匹配所有的 Unicode 单词字符串:
const str = ;
console.log(/\p{Alphabetic}/u.test(str)); // → true
// the \w shorthand cannot match
console.log(/\w/u.test(str)); // → false
同样有三个负向的 Unicode 属性转义模板 \P{…}:
console.log(/\P{Number}/u.test(㉛)); // → false
console.log(/\P{Number}/u.test( )); // → true
console.log(/\P{Alphabetic}/u.test(㉛)); // → true
console.log(/\P{Alphabetic}/u.test( )); // → false
除了字母和数字之外,Unicode 属性转义中还能采用其他一些属性。
以上这几个优点的支持情形:
ES10 新优点
1.Array.prototype.flat()
多维数组是一种常见的数据格式,特别是在展开数据检索的时候。将多维数组打平是个常见的需求。通常他们能够实现,但是比不上优雅。
flat() 方式会按照三个可选定的深度递归遍历数组,并将所有原素与遍历到的子数组中的原素合并为三个新数组回到。
newArray = arr.flat(depth)
// depth 是选定要提取嵌套数组的结构深度,预设值为 1
接下来他们看三个例子:
const numbers1 = [1, 2, [3, 4, [5, 6]]]
console.log(numbers1.flat())// [1, 2, 3, 4, [5, 6]]
const numbers2 = [1, 2, [3, 4, [5, 6]]]
console.log(numbers2.flat(2))// [1, 2, 3, 4, 5, 6]
上面三个例子说明 flat 的模块没有设置,取预设值 1,也就是说只扁平化第一级;当 flat 的模块大于等同于 2,回到值就是 [1, 2, 3, 4, 5, 6] 了。
Array.prototype.flat 的支持情形:
2.Array.prototype.flatMap()
有了 flat 方式,那自然而然就有 Array.prototype.flatMap 方式,flatMap() 方式首先采用映射函数映射每个原素,然后将结果压缩成三个新数组。从方式的名字上也能看出来它包涵两部分功能三个是 map,三个是 flat(深度为 1)。
let arr = [1, 2, 3]
console.log(arr.map(item => [item * 2]).flat()) // [2, 4, 6]
console.log(arr.flatMap(item => [item * 2])) // [2, 4, 6]
实际上 flatMap 是综合了 map 和 flat 的操作,因此 它也只能打平一 层。
Array.prototype.flatmap 的支持情形:
3.Object.fromEntries()
Object.fromEntries 这个新的 API 实现了与 Object.entries 相反的操作。这使得依照对象的 entries 很容易得到 object。
const object = { x: 23, y:24 };
const entries = Object.entries(object); // [[x, 23], [y, 24]]
const result = Object.fromEntries(entries); // { x: 23, y: 24 }
ES2017 引入了 Object.entries, 这个方式能将对象转换为数组, 这样对象就能采用数组原型中的众多内置方式,比如 map, filter、reduce,举个例子,他们想提取下列对象 obj 中所有 value 大于 21 的键值对,如何操作呢?
// ES10 以后
const obj = {
a: 21,
b: 22,
c: 23
}
console.log(Object.entries(obj)) // [[a,21],[“b”, 22],[“c”, 23]]
let arr = Object.entries(obj).filter(([a, b]) => b > 21) // [[“b”, 22],[“c”, 23]]
let obj1 = {}
for (let [name, age] of arr) {
obj1[name] = age
}
console.log(obj1) // {b: 22, c: 23}
上例中得到了数组 arr,想再次转化为对象,就需要手动写一些代码来处理,但是有了 Object.fromEntries() 就很容易实现。
// 用 Object.fromEntries() 来实现
const obj = {
a: 21,
b: 22,
c: 23
}
let res = Object.fromEntries(Object.entries(obj).filter(([a, b]) => b > 21))
console.log(111, res) // {b: 22, c: 23}
Object.fromEntries() 的支持情形:
4.String.trimStart 和 String.trimEnd
移除开头和结尾的空格,以后他们用正则抒发式来实现,现在 ES10 新增了三个新优点,让这显得更简单!
trimStart() 方式从数组的开头删除空格,trimLeft() 是此方式的别名。
let str = 前端工匠
console.log(str.length) // 6
str = str.trimStart()
console.log(str.length) // 5
let str1 = str.trim() // 清除前后的空格
console.log(str1.length) // 4
str.replace(/^\s+/g, ) // 也能用正则实现开头删除空格
trimEnd() 方式从三个数组的右端移除空白字符串,trimRight 是 trimEnd 的别名。
let str = 浪里行舟
console.log(str.length) // 6
str = str.trimEnd()
console.log(str.length) // 5
let str1 = str.trim() // 清除前后的空格
console.log(str1.length) // 4
str.replace(/\s+$/g, ) // 也能用正则实现右端移除空白字符
String.trimStart 和 String.trimEnd 两者兼容性一致,下图以 trimStart 为例:
5.String.prototype.matchAll
假如三个正则抒发式在数组里面有多个匹配,现在一般采用 g 修饰符或 y 修饰符,在循环里面逐一取出。
function collectGroup1 (regExp, str) {
const matches = []
while (true) {
const match = regExp.exec(str)
if (match === null) break
matches.push(match[1])
}
return matches
}
console.log(collectGroup1(/”([^”]*)”/g, `”foo” and “bar” and “baz”`))
// [ foo, bar, baz ]
值得称赞注意的是,假如没有修饰符 /g, .exec() 只回到第三个匹配。现在通过 ES9 的 String.prototype.matchAll 方式,能一次性取出所有匹配。
function collectGroup1 (regExp, str) {
let results = []
for (const match of str.matchAll(regExp)) {
results.push(match[1])
}
return results
}
console.log(collectGroup1(/”([^”]*)”/g, `”foo” and “bar” and “baz”`))
// [“foo”, “bar”, “baz”]
上面代码中,由于 string.matchAll(regex) 回到的是遍历器,因此能用 for…of 循环取出。
String.prototype.matchAll 的支持情形:
6.try…catch
在 ES10 中,try-catch 语句中的模块变为了一个可选项。以前他们写 catch 语句时,必须传递三个异常模块。这就意味着,即便他们在 catch 里面根本不需要用到这个异常模块也必须将其传递进去
// ES10 以后
try {
// tryCode
} catch (err) {
// catchCode
}
这里 err 是必须的模块,在 ES10 能省略这个参数:
// ES10
try {
console.log(Foobar)
} catch {
console.error(Bar)
}
try…catch 的支持情形:
7.BigInt
JavaScript 所有数字都保存成 64 位浮点数,这给数值的则表示带来了两大限制。其一数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确则表示的,这使得 JavaScript 不适合展开科学和金融方面的精确计算。并有大于或等同于 2 的 1024 次方的数值,JavaScript 无法则表示,会回到 Infinity。
// 超过 53 个二进制位的数值,无法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// 超过 2 的 1024 次方的数值,无法则表示
Math.pow(2, 1024) // Infinity
现在 ES10 引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来则表示整数,没有位数的限制,任何位数的整数都能精确则表示。
创建 BigInt 类型的值也非常简单,只需要在数字后面加上 n 即可。例如,123 变为 123n。也能采用全局方式 BigInt(value) 转化,入参 value 为数字或数字数组。
const aNumber = 111;
const aBigInt = BigInt(aNumber);
aBigInt === 111n // true
typeof aBigInt === bigint // true
typeof 111 // “number”
typeof 111n // “bigint”
假如算上 BigInt,JavaScript 中原始类型就从 6 个变为了 7 个。
BooleanNullUndefinedNumberStringSymbol (new in ECMAScript 2015)BigInt (new in ECMAScript 2019)BigInt 的支持情形:
8.Symbol.prototype.description
他们知道,Symbol 的描述只被存储在外部的 [[Description]],没有直接对外暴露,他们只有调用 Symbol 的 toString() 时才能读取这个属性:
Symbol(desc).description; // “desc”
Symbol().description; // “”
Symbol().description; // undefined
Symbol.prototype.description 的支持情形:
9.Function.prototype.toString()
ES2019 中,Function.toString() 发生了变化。以后执行这个方式时,得到的数组是去空白符号的。而现在,得到的数组呈现出原本源码的样子:
function sum(a, b) {
return a + b;
}
console.log(sum.toString());
// function sum(a, b) {
// return a + b;
// }
Function.prototype.toString() 的支持情形:
参考资料
再学 JavaScript ES(6-10)全版语法大全ECMAScript 6 入门MDN 文档ES2019 新优点你知道什么样?ES2018 新优点——每个 JS 开发者都需要了解细解 JavaScript ES7 ES8 ES9 新优点责任编辑转载自微信公众号:前端工匠