四千字长文 总结 从 ES7 到 ES10 到底新增了什么属性?背景 估计每一个前端人都知道阮一峰,他的 ES6入门教程
背景
估计每一个前端人都知道阮一峰,他的 ES6入门教程 是我们入门JS必读的书籍之一,每每读到我们都能从中学到一些新的点,对于我们学习真的帮助很大很大。
关于 es6
我们在开发的过程中,遇到的情况很多很多,但是我们对于 ES7 ~ ES10 中到底新增了哪些内容呢?我们其实不一定心里这么清楚。所以我们到底趁着这个机会,用心学习一下 ES7 ~ ES10 到底新增了哪些内容,正好我们可以温故而知新。
一、ES7(ECMAScript 2016)主要新增内容
-
指数运算符(* *) :
- 功能:用于进行幂运算,即求一个数的指定次方。例如,的结果是,其效果与使用
Math.pow(2, 3)
方法是相同的,但写法上更加简洁直观。 - 优先级:该运算符的优先级较高,在表达式中会先于乘法运算符 、加法运算符 等进行运算。例如, 的结果是 ,先计算 ,再计算 ;如果按照常规理解不考虑优先级,可能会认为先计算 ,再计算 ,这是错误的理解。
- 功能:用于进行幂运算,即求一个数的指定次方。例如,的结果是,其效果与使用
-
Array.prototype.includes()
方法:- 功能:用于判断一个数组是否包含一个指定的值,返回一个布尔值。如果数组中存在指定的值,则返回
true
;否则,返回false
。例如,对于数组const numbers = [1, 2, 3, 4, 5]
,numbers.includes(3)
的结果是true
,numbers.includes(6)
的结果是false
。 - 参数:该方法接收两个参数,第一个参数是要搜索的值,第二个参数是可选的搜索的开始索引。如果不提供第二个参数,则默认从数组的开头开始搜索。例如,对于数组
const arr = [1, 2, 3, 4, 5]
,arr.includes(3, 2)
会从索引为的位置开始搜索,即判断数组中从索引开始往后是否包含值,结果是true
;而arr.includes(3, 3)
则从索引为的位置开始搜索,结果是false
。 - 与
indexOf
的区别:indexOf
方法返回的是被查找元素在数组中的索引,如果不存在则返回 ;而includes
方法只返回布尔值。另外,includes
方法可以查找NaN
,而indexOf
方法不能。例如,(NaN).includes(NaN)
的结果是true
,而(NaN).indexOf(NaN)
的结果是 34。
- 功能:用于判断一个数组是否包含一个指定的值,返回一个布尔值。如果数组中存在指定的值,则返回
除了以上两个主要特性外,ES7 在语法规范等方面可能还有一些细节上的改进和完善,但这两个新特性是比较突出和常用的。这些新特性使得 JavaScript 语言在功能和表达能力上得到了进一步增强,也为开发者提供了更便捷、高效的编程方式。
-
函数参数的尾随逗号(Trailing Commas) :
- 在 ES7 之前,在函数定义或调用时,参数列表中最后一个参数后面不能有逗号,否则会报错。而 ES7 允许在函数参数列表的最后一个参数后面添加逗号,这使得在添加、删除或重新排序参数时更加方便,也更符合一些版本控制系统的 diff 和 merge 操作。例如:
function myFunction(param1, param2,) {
// 函数体
}
-
异常处理的改进:
- 虽然 ES7 本身没有对异常处理机制进行重大的结构性改变,但随着
async/await
的引入,异常处理变得更加直观和易于理解。在async
函数中,可以使用try/catch
块来捕获异步操作中的错误,这比使用Promise
的.then()
和.catch()
方法更加清晰和简洁,并且与传统的同步代码的异常处理方式更加一致,提高了代码的可读性和可维护性。例如:
- 虽然 ES7 本身没有对异常处理机制进行重大的结构性改变,但随着
async function fetchData() {
try {
const response = await fetch('https://example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
return null;
}
}
-
对对象操作方法的增强:
Object.entries()
和Object.values()
方法的引入,方便了对对象属性的操作和遍历。Object.entries()
返回一个给定对象自身可枚举属性的键值对数组,Object.values()
返回一个给定对象自身的所有可枚举属性值组成的数组。这两个方法使得在处理对象属性时,可以更方便地获取属性的键或值,并且在与数组相关的操作和一些函数式编程场景中非常有用。例如:
const person = { name: 'John', age: 30, city: 'New York' };
const entries = Object.entries(person);
console.log(entries); // [['name', 'John'], ['age', 30], ['city', 'New York']]
const values = Object.values(person);
console.log(values); // ['John', 30, 'New York']
二、ES8(ECMAScript 2017)主要新增内容
-
异步函数(Async/Await) :
async
函数:使用async
关键字定义的函数被称为异步函数。异步函数会返回一个Promise
对象,用于表示异步操作的最终完成或失败。这使得异步代码的编写方式更接近同步代码,提高了代码的可读性和可维护性。例如:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
}
}
await
表达式:必须写在 async
函数中,用于等待一个 Promise
对象的完成。await
右侧的表达式通常是一个 Promise
对象,它会暂停 async
函数的执行,直到 Promise
对象完成。如果 Promise
对象成功完成,await
表达式会返回 Promise
对象的结果;如果 Promise
对象失败,await
表达式会抛出一个错误,需要使用 try/catch
块来捕获处理
- 字符串填充(String Padding) 4:
padStart()
方法:用于在字符串的开头填充指定的字符,直到字符串达到指定的长度。该方法接受两个参数,第一个参数是目标长度,即填充后的字符串长度;第二个参数是可选的填充字符,如果不提供第二个参数,则默认使用空格填充。例如:
const str = '123';
const paddedStr = str.padStart(5, '0');
console.log(paddedStr); // '00123'
padEnd()
方法:用于在字符串的结尾填充指定的字符,直到字符串达到指定的长度。其参数与padStart()
方法类似。例如:
const str = 'hello';
const paddedStr = str.padEnd(10, ');
console.log(paddedStr); // 'hello ';
三、ES9(ECMAScript 2018)主要新增内容
-
异步迭代(Async Iteration) :允许使用
for await...of
循环遍历异步可迭代对象。 -
Promise.finally() :无论 Promise 是成功还是失败,
finally
方法中的回调函数都会被执行。 -
Rest/Spread 属性(对象) :可以在对象中使用展开运算符,实现对象属性的合并和复制。
-
正则表达式命名捕获组和反向引用:可以为正则表达式中的捕获组命名,方便在匹配结果中引用。
-
异步迭代:
- 在此之前,虽然有了
async/await
让异步函数的编写更接近同步代码,但对于异步的迭代操作还不够方便。ES9 新增了异步迭代器和for-await-of
循环,允许更方便地处理异步可迭代对象。例如,对于一个返回Promise
的异步数据源,可以使用for-await-of
循环来依次获取每个异步操作的结果,这在处理大量异步数据操作时非常有用,代码更加简洁和易读。
- Promise 改进:
Promise.finally()
方法:该方法用于指定无论Promise
是成功完成还是被拒绝,都会执行的代码。这在需要进行一些无论Promise
最终状态如何都必须执行的操作时非常方便,比如在Promise
执行完成后关闭加载指示器、释放资源等。例如:
const myPromise = new Promise((resolve, reject) => {
// 模拟一个异步操作
setTimeout(() => {
resolve('操作成功');
}, 2000);
});
myPromise.then(result => {
console.log(result);
}).catch(error => {
console.log(error);
}).finally(() => {
console.log('无论成功或失败,都会执行此代码');
});
- 正则表达式命名捕获组和反向引用6:
- 命名捕获组:允许在正则表达式中为捕获组指定名称,使得匹配结果中的捕获组更易于理解和使用。以前使用正则表达式的捕获组时,只能通过索引来访问捕获到的内容,而命名捕获组可以通过名称来访问,提高了代码的可读性和可维护性。例如:
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const dateString = '2023-10-15';
const match = dateString.match(regex);
console.log(match.groups.year); // "2023"
console.log(match.groups.month); // "10"
console.log(match.groups.day); // "15"
- 反向引用:在正则表达式中,可以使用
\k<name>
的形式来引用之前命名的捕获组,这在需要对匹配到的内容进行复杂的替换或验证时非常有用。例如,验证一个字符串是否是重复的单词,可以使用反向引用来检查:
const regex = /^(?<word>\w+)\s+\k<word>$/;
const str1 = 'hello hello';
const str2 = 'world hello';
console.log(regex.test(str1)); // true
console.log(regex.test(str2)); // false
- 剩余参数和扩展运算符的增强:
- 在对象属性初始化和函数参数中,可以更方便地使用剩余参数和扩展运算符。例如,在对象属性初始化时,可以使用扩展运算符来合并多个对象的属性:
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const mergedObj = {...obj1,...obj2 };
console.log(mergedObj); // { a: 1, b: 2, c: 3, d: 4 }
- 在函数参数中,可以使用剩余参数来接收不确定数量的参数,并结合扩展运算符来传递参数。例如:
function myFunction(...args) {
const newArgs = [...args, 5, 6 ];
console.log(newArgs);
}
myFunction(1, 2, 3, 4); // [1, 2, 3, 4, 5, 6]
四、ES10(ECMAScript 2019)主要新增内容
-
数组方法增强:
flat()
方法:用于将数组中的嵌套数组展开,使其成为一个一维数组。该方法可以接受一个可选的参数depth
,用于指定展开的深度。如果不指定depth
参数,默认值为1
,即只展开一层嵌套的数组。例如:
const arr = [1, 2, [3, 4], [[5, 6]]];
console.log(arr.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(arr.flat(2)); // [1, 2, 3, 4, 5, 6]
flatMap()
方法:该方法结合了map()
和flat()
的功能,先对数组的每个元素进行映射操作,然后将结果展开成一个一维数组。这在需要对数组进行映射操作并且同时展开嵌套数组的情况下非常有用。例如:
const arr = ['a', 'b', 'c'];
const result = arr.flatMap(x => [x, x]);
console.log(result); // ['a', 'a', 'b', 'b', 'c', 'c']
Object.fromEntries()
方法123:该方法接收一个可迭代对象(如数组或Map
),其中的元素是键值对形式的数组,然后将其转换为一个对象。这个方法与Object.entries()
方法相对应,Object.entries()
是将对象转换为键值对数组,而Object.fromEntries()
则是将键值对数组转换回对象。例如:
const entries = [['name', 'John'], ['age', 30]];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'John', age: 30 }
- 字符串方法扩展:
trimStart()
方法:用于去除字符串开头的空白字符。以前只有trim()
方法可以去除字符串两端的空白字符,而trimStart()
方法则专门用于去除字符串开头的空白。例如:
const str = ' Hello';
console.log(str.trimStart()); // 'Hello'
trimEnd()
方法:用于去除字符串结尾的空白字符,与trimStart()
方法类似,专门针对字符串结尾的空白进行去除。例如:
const str = 'Hello ';
console.log(str.trimEnd()); // 'Hello'
Symbol.prototype.description
属性123:在创建Symbol
时,可以传入一个描述信息作为参数。在 ES10 之前,要获取Symbol
的描述信息需要通过Symbol.toString()
方法,但该方法返回的字符串包含了Symbol()
的前缀。而Symbol.prototype.description
属性则直接返回创建Symbol
时传入的描述信息。例如:
const sym = Symbol('MySymbol');
console.log(sym.description); // 'MySymbol'
- 可忽略的
catch
参数:在try/catch
语句中,catch
块以前必须接收一个参数来表示捕获到的错误对象。但在 ES10 中,如果不需要使用这个错误对象,可以省略catch
块的参数。例如:
try {
// 可能会抛出错误的代码
} catch {
// 不需要处理错误对象的情况下,可以省略参数
}
-
数组排序的稳定性:在 ES10 之前,数组的
sort()
方法的排序算法是不稳定的,即对于相等的元素,排序后的顺序可能会发生变化。ES10 对数组的sort()
方法进行了改进,使其在默认情况下是稳定的排序算法。这意味着如果两个元素相等,它们在排序后的顺序将与它们在原始数组中的顺序保持一致。 -
Function.prototype.toString()
的改进:在 ES10 中,Function.prototype.toString()
方法返回的字符串会保留函数的原始格式,包括注释和空白字符。在以前的版本中,这些注释和空白字符会被去除。这对于调试和代码分析非常有用。例如:
function myFunction() {
// 这是一个注释
return 'Hello';
}
console.log(myFunction.toString());
- 标准化的
globalThis
对象:在不同的 JavaScript 环境中,获取全局对象的方式可能不同。例如在浏览器中可以使用window
对象,在 Node.js 中可以使用global
对象。ES10 引入了globalThis
对象,它提供了一种统一的方式来访问全局对象,无论在什么环境下都可以使用globalThis
来获取全局对象。
<!DOCTYPE html>
<html>
<body>
<script>
console.log(globalThis === window); // true
// 设置一个全局变量
globalThis.myGlobalVar = 'This is a global variable';
function testFunction() {
console.log(globalThis.myGlobalVar);
}
testFunction();
</script>
</body>
</html>
console.log(globalThis === global); // true
globalThis.myGlobalVar = 'This is a global variable in Node.js';
console.log(globalThis.myGlobalVar);
在这个例子中,首先验证了在不同环境下 globalThis
与对应的全局对象的关系。然后在不同环境中设置了一个全局变量 myGlobalVar
,并在一个函数中访问这个全局变量,展示了 globalThis
作为统一访问全局对象的方式。
总结
其实我们这么看从 ES7~ES9,整个过程中,发展的历程,语法越来越可读,越来越与实际的开发情况相贴合了。
- 操作符与方法的改进
举个例子 ES7 引入了求幂运算符**
,这是对数学运算的直接支持,相比使用 Math.pow()
函数更加简洁直观,方便了开发者进行数值计算,提高了代码的编写效率。例如 2 ** 10
可以很方便地计算出 2 的 10 次方。
- 数据结构操作的优化
- ES7 中数组的
includes()
方法允许直接判断数组是否包含某个指定的值,相比indexOf()
方法更加直观和语义化。indexOf()
方法需要通过判断返回值是否为-1
来确定元素是否存在,而includes()
方法直接返回true
或false
,简化了数组元素查找的代码逻辑 - ES9 中的
Object.fromEntries()
方法与Object.entries()
方法相对应,Object.entries()
将对象转换为键值对数组,而Object.fromEntries()
则将键值对数组转换回对象,这为对象和数组之间的相互转换提供了便捷的方式,方便了数据的处理和操作
- 异步编程的优化与完善
ES8 引入的 async/await
是异步编程的重大改进,它使异步代码看起来更像同步代码,增强了代码的可读性和可维护性。在 ES8 之前,使用 Promise
虽然解决了回调地狱的问题,但代码中仍然充斥着大量的 .then()
方法,语义不够清晰。而 async/await
让开发者能够以更接近同步代码的方式编写异步逻辑,并且可以使用 try/catch
来捕获异步操作中的错误,提高了异步代码的编写效率和错误处理能力
- 对对象操作的扩展与深入
ES8 中 Object.values()
和 Object.entries()
方法的引入,作为对 Object.keys()
的补充,提供了更方便的方式来获取对象自身属性的值和键值对数组,方便了对对象属性的遍历和操作。这使得开发者在处理对象数据时,能够更灵活地获取和操作对象的属性值。
- 开发便利性的提升:ES8 中
String
新增的padStart()
和padEnd()
方法,允许将空字符串或其他字符串添加到原始字符串的开头或结尾,这在字符串格式化和填充场景中非常实用,例如在处理日期、时间、数字等需要固定长度显示的场景中,可以方便地进行字符串的填充操作
那么今天的知识点就到这里了,希望可以帮到你们吧。如果内容帮到你们的话,欢迎点赞留言,这样对我继续创作是莫大的鼓励和支持,再次谢谢大家了
转载自:https://juejin.cn/post/7425626637835468839