ES7~ES12新特性基础笔记整理
![猪哥](https://picx.zhimg.com/v2-1bf58b0bb74a80d1bda7b3b094ab9e53_l.jpg?source=172ae18b)
![](https://picx.zhimg.com/v2-4812630bc27d642f7cafcd6cdeca3d7a.jpg?source=88ceefae)
ES7,Array.prototype.includes
>>>>>includes方法是用来检测数组中是否包含某个元素,返回相对应得布尔类型值
- 跟indeOf()方法有点类似
let school = ['清华大学','北京大学','浙江大学'];
console.log(school.includes('清华大学')); // true
ES7,求幂运算(**)
>>>>>类似方法:1.自定义递归函数,2.Math.pow()方法
let num01 = Math.pow(2,10); //Math.pow()方法
let num02 = 2 ** 10; // 幂运算(**)
console.log(num01,num02); // 1024,1024
ES8,async函数,await表达式
(ES8async函数)
>>>>>Async 和 Awaiit 是 Promise 的扩展,JavaScript 是单线程的,使用 Promise 之后可以使异步操作的书写更简洁,而 Async 使 Promise 像同步操作。
async函数的返回值是Pormise对象
Promise对象的结果由async函数执行的返回值所决定
// 在申明函数的前面加"async"
async function test() {
// 只要返回的不是一个Promise类型对象,则返回的结果是成功的Promise对象的,哪怕只有“return”
return 'javascript';
throw new Error('error');
- 如果返回的是一个Promise对象
// 在申明函数的前面加"async"
async function test() {
return new Promise((resolve,reject) => {
// Promise对象的状态由函数内部return语句决定,即由resolve和reject的最终结果所决定
resolve('success!');
reject('errro!');
- 这时候调用then()方法
// 在申明函数的前面加"async"
async function test() {
return new Promise((resolve,reject) => {
//resolve('success!');
reject('errro!');
test().then(value => {
console.log(value); //如果状态为resolve则异步返回value
},reason => {
console.warn(reason); //如果状态为reject则异步返回value
(ES8await表达式)
>>>>>Await 放置在 Promise 调用之前,强制后面的代码等待,直到 Promise 对象 resolve,得到 resolve 的值作为 await 表达式的运算结果
await必须写在async函数中
await右侧的表达式一般为Promise对象
await返回的是Promise成功的值
await的Promise失败了,则会抛出异常,需要通过try-catch捕获结果
- 成功的值
let pro = new Promise((resolve,reject) => {
resolve('接受!');
async function test() {
let result = await pro;
console.log(result); // 返回的就是Promise成功的值
test();
- 失败的值(用try-catch捕获结果)
let pro = new Promise((resolve,reject) => {
reject('失败!');
async function test() {
try {
let result02 = await pro;
console.log(result02); //失败!
} catch(e) {
console.log(e);
test();
(ES8async函数与await表达式结合运用)
// 单独写在js文件中,用终端直接运行
let fs = require('fs');
function readFile01() {
return new Promise((resolve,reject) => {
fs.readFile('./11.txt',(err,data) => {
if(err) reject(err);
resolve(data);
function readFile02() {
return new Promise((resolve,reject) => {
fs.readFile('./22.txt',(err,data) => {
if(err) reject(err);
resolve(data);
function readFile03() {
return new Promise((resolve,reject) => {
fs.readFile('./33.txt',(err,data) => {
if(err) reject(err);
resolve(data);
async function result() {
let read01 = await readFile01();
let read02 = await readFile02();
let read03 = await readFile03();
console.log(read01 + '\r');
console.log(read02 + '\r');
console.log(read03 + '\r');
result();
- 两者结合发送Ajax请求
// 使用ES6 Promise的then方法
// 发送get请求,返回Promise对象
function XML(url) {
let x = new XMLHttpRequest();
return new Promise((resolve,reject) => {
x.open('get',url);
x.send();
x.onreadystatechange = () => {
if(x.readyState === 4) {
if(x.status >= 200 < 300) {
resolve(x.response);
}else {
reject(x.status);
let result = XML('./22.txt').then(value => {
console.log(value); // (控制台输出文本结果)
},reason => {
// 使用async和await结合使用
// 发送get请求,返回Promise对象
function XML(url) {
let x = new XMLHttpRequest();
return new Promise((resolve,reject) => {
x.open('get',url);
x.send();
x.onreadystatechange = () => {
if(x.readyState === 4) {
if(x.status >= 200 < 300) {
resolve(x.response);
}else {
reject(x.status);
async function result() {
let result = await XML('./22.txt');
console.log(result);
result(); // (控制台输出文本结果)
ES8,对象方法扩展
>>>>>包含【Object.value、Object.entries、Object.getOwnPropertyDescriptors】
//---
let school = {
name:'TsingHua',
subject:['math','chinese']
// 获取对象所有的键值
console.log(Object.keys(school)); // ['name', 'subject']
console.log(Object.values(school)); // ['TsingHua', Array(2)]
console.log(Object.entries(school)); // [Array(2), Array(2)]
// 方便创建Map
let result = new Map(Object.entries(school));
console.log(result.get('subject')); // Object.entries返回的是一个数组['math','chinese']
Object.keys()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
Object.values()
方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for-in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
// getOwnPropertyDescriptors返回的是对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(school));
// Object.getOwnPropertyDescriptors()返回的就是以下对象,可以更深层次地对对象进行克隆
let obj = Object.create(null, {
name:{
value:'TsingHua',
writable:true,
configurable:true,
ES9,扩展运算符和Rest参数
>>>>>Rest参数与spread扩展运算符在ES6中已经加入到原生js里面,但是ES6中只针对于数组,在ES9中它们也支持对象
// Rest参数语法允许我们将一个剩余参数表示为一个数组
function foo(a, b, ...rest) {
return [a,b,...rest];
let result = foo(1, 2, 3, 4, 5);
console.log(result); //[1, 2, 3, 4, 5]
// ES9的Rest参数支持对象
let university01 = {
name:'清华大学',
let university02 = {
address:'BeiJing',
let university03 = {
subject:'ComputerScience'
let result = {...university01,...university02,...university03};
console.log(result); //{name: '清华大学', address: 'BeiJing', subject: 'ComputerScience'}
跟数组一样,Rest参数只能在声明的结尾处使用
展开操作符则是将数组转换成可传递给函数的单独参数
ES9,正则扩展,命名捕获分组
正则扩展,命名捕获分组---分组匹配到的结果进行命名,对结果进行处理>>>>>
// 没有进行命名捕获分组的处理
//声明一个字符串
let Link = ' <a href="https://www.baidu.com">百度</a>';
//我们可以提取Link字符串中的网址以及标签里面的文本信息
let reg01 = /<a href = "(.*)">(.*)<\/a>/;
let result01 = reg01.exec(Link01);
console.log(result01);
-
第0个元素是正则整个匹配到的结果
第1个元素是正则是第一个“(.*)”匹配到的结果
第2个元素是正则是第一个“(.*)”匹配到的结果
其中2和3都可以称为捕获>>>得到如下结果
console.log(result01[1]); //https://www.baidu.com
console.log(result01[2]); //百度
------------------------------------------分割线--------------------------------------------
// 运用分组
let Link02 = '<a href="https://www.baidu.com"></a>';
// -----规定语法-----
//在未运用分组的基础上,在需要捕获的括号内部添加 “?<捕获标识符>.*”
let reg02 = /<a href="(?<url>.*)">(?<text>.*)<\/a>/
const result02 = reg02.exec(Link02);
console.log(result02.groups.url);
- 上面的0、1、2都还在,在“groups”中多了上面运用分组所捕获的数据
console.log(result02.groups.url); //https://www.baidu.com
console.log(result02.groups.text); //百度
这样一来我们提取正则所捕获的数据会更加地方便和便于维护
ES9,正则扩展,反向断言
反向断言,用于判断匹配结果是否正确
// 先使用正向断言
// 提取 “TsinghuaUniversity2030”
let content = '清华大学TsinghuaUniversity2030级';
// 正向断言
let regExctart = /\w+(?=级)/;
let result = regExctart.exec(content);
console.log(result[0]); //TsinghuaUniversity2030
// 再使用反向断言
// 提取 “TsinghuaUniversity2030”
let content = '清华大学TsinghuaUniversity2030级';
// 反向断言
let regExctart = /(?<=学)\w+/;
let result = regExctart.exec(content);
console.log(result[0]); //TsinghuaUniversity2030
ES9,正则扩展,dotAll模式
JavaScript正则表达式中点(.)是一个特殊字符,它可以匹配除了一下条件的任意字符。
四个字节的UTF-16字符
换行符(\n)
回车符(\r)
行分隔符
段分隔符
为了使点(.)可以匹配任意字符,ES2018引入新的修饰符s。这种模式被称为dotAll模式,根据字面意思便是dot(.)匹配一切字符。
const re = /foo.bar/s
// 判断是否启用dotAll模式
console.log(re.dotAll) // true
正则表达式中,点(.)是一个特殊字符,代表除了换行符以外的任意单个字符。
<ul>
<a>《JavaScript数据结构》</a>
<p>出版日期:2020-10-24</p>
<a>《JavaScript算法》</a>
<p>出版日期:2021-10-24</p>
往常的做法在捕捉的时候要把多余的空格添加上去
dot正好可以更方便的处理这个问题
let reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
let result;
while(result = reg.exec(bookStr)) {
console.log(result);
let data = []; //定义一个数组
while(result = reg.exec(bookStr)) {
data.push(result[1],result[2])
console.log(data);
ES10,对象扩展方法Object.fromEntries
Object.fromEntries()方法把键值对列表转换为一个对象,该方法创建对象,参数接收二维数组和Map>>>>>
// 定义一个二维数组
let content = Object.fromEntries([
['name', 'javascript算法'],
['data', '2021-10-24']
console.log(content);
// 定义一个Map对象
let mapObj = new Map();
// 传入参数
mapObj.set('name', 'javascript算法');
let result = Object.fromEntries(mapObj);
console.log(result);
- 用回ES8的Object.entries方法
let arr = Object.entries({
name: 'javascipt算法'
console.log(arr);
entries()方法可以把对象转化为数组,Object.fromEntries则把二维数组转化为对象
>>>>>Object.fromEntries和ES8里面的entries()方法为相互的逆运算(互逆)
ES10,字符串扩展方法【trimStart,trimEnd】
trimStart、trimEnd可以清楚多余的空白>>>>>
//声明一个字符串
let arr = ' JavaScript '; //前后都有空白
console.log(arr);
console.log(arr.trimStart()); //清楚前面的空白
console.log(arr.trimEnd()); //清楚后面的空白
>>>>>trimStart清除左侧空白,trimEnd清楚右侧空白
ES10,数组方法扩展flat、flatMap
flat()方法可以将多维数组展平成一维数组>>>>>
let arrFlat01 = [1,2,3,[4,5,6]];
console.log(arrFlat01.flat()); //[1, 2, 3, 4, 5, 6]
flat()里面传递的参数为数字,数组的深度
let arrFlat02 = [1,2,3,[4,5,6,[7,8,9]]]; //仅要一层深度
console.log(arrFlat02.flat(1)); // [1, 2, 3, 4, 5, 6, Array(3)]---Array(3)包含就是数组[7,8,9]
let arrFlat03 = [1,2,3,[4,5,6,[7,8,9]]]; //需要两层深度
console.log(arrFlat03.flat(2)); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>flatMap()方法把原来的数组处理后返回一个新的一维数组。首先使用映射函数映射每个元素,用数组包裹起来,然后将结果压缩成一个新数组。它与map连着深度值为1的flat几乎相同, 但是flatMap通常在合并成一种方法的效率稍微高一些 >>>
- map()和flatMap()示例
let content = [1,2,3,4,5,6];
let result = content.map(item => [item * 10]);
console.log(result);
- 这个时候如果想把上面的map()示例结果变成一维数组来使用,可以使用flatMap()
let content = [1,2,3,4,5,6];
let result = content.flatMap(item => [item * 10]);
console.log(result);
ES10,Symbol.prototype.description
description是一个只读属性,它会返回Symbol对象的可描述的字符串>>>>>>
let content = Symbol('清华大学');
let result = content.description;
console.log(result); //清华大学
ES11,私有属性
私有属性只允许在类的内部调用,且不允许在在类中。私有属性通过 # 申明
class School {
// 定义公有属性
name;
// 定义私有属性
income;
constructor(name, income) {
this.name = name;
this.#income = income;
let TsingHua = new School('清华大学', '1w');
console.log(TsingHua); // School{name: '清华大学', #income: '1w'}
//打印输入#incom这个私有属性
console.log(TsingHua.#income); //Private field '#income' must be declared in an enclosing class.
私有属性是无法访问得到的,在类的外部访问不到私有属性
// 我们要在class的外部进行定义
intro() {
console.log(this.#income);
// 调用
TsingHua.intro(); // 1w
ES11,Promise.allSettled方法
Promise.allSettled方法返回一个在所有给定的promise都已经fulfilled或reject后的promise,并带有一个对象数组,每个对象表示对应的promise结果>>>>>
let content = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('内容一');
let content02 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('内容一');
// 调用 Promise.all()方法
let result = Promise.all([content, content02]);
console.log(result);
按使用场景来区分:
如果想每个异步任务都想得到同一个结果,使用Promise.allSettled方法
如果想每个异步任务要求每个都成功才能够继续往下执行,使用Promise.all()方法
ES11,String.prototype.matchAll()方法
>>>>>String.prototype.matchAll()方法,用来得到正则批量匹配得到的结果
// String.prototype.matchAll()方法
let str =
<a>《JavaScript数据结构》</a>
<p>出版日期:2020-10-24</p>
<a>《JavaScript算法》</a>
<p>出版日期:2021-10-24</p>
</ul>`;
let reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg;
let result = str.matchAll(reg);
console.log(result);
// 运用for-of进行遍历得到结果
for (let i of result) {
console.log(i);
// 运用扩展运算符得到结果
let arr = [...result];
console.log(arr);
ES11,可选链操作符
可选链操作符--->(?.)的组合,允许读取位于连接对象链深处的属性的值,即可读取对象类型的参数,短路返回值是undefined,且与函数调用一起使用时,如果给定的函数不存在,则返回undefined>>>>>
function language(content) {
let useLauguage = content && content.one.Good && content.one.purpose;
//可以获取到对应的值
console.log(useLauguage);
language({
one: {
Good: 'javascript',
purpose: 'HTML'
tow: {
Good: 'c++',
purpose: 'app'
function language(content) {
// 注释原来的条件判断,直接获取
// let useLauguage = content && content.one.Good && content.one.purpose;
let useLauguage = content.one.Good;
console.log(useLauguage);
//直接调用报错:Cannot read properties of undefined (reading 'one').
language();
// language({
// one:{
// Good:'javascript',
// purpose:'HTML'
// },
// tow:{
// Good:'c++',
// purpose:'app'
// }
// })
- 运用可选链操作符
function language(content) {
let useLauguage = content?.one;
console.log(useLauguage);
language({
one:{
Good:'javascript',
purpose:'HTML'
tow:{
Good:'c++',
purpose:'app'
//
function language(content) {
let useLauguage = content?.one;
console.log(useLauguage);
// 就算直接调用也不会报错,控制台显示undefined
language();
ES11,动态import
在此之前,我们都是使用静态的import引入页面中所需要的功能模块。动态import的作用就算按需要来加载需要引入的功能模块>>>>>
1.首先在主页部分创建一个按钮实现点击按钮弹出提示框
//HTML部分
<!DOCTYPE html>
<meta charset="utf-8">
<title></title>
<script src="app.js" type="module" charset="utf-8"></script>
</head>
<button type="button" id="btn">我是一个按钮</button>
</body>
</html>
2.运用动态import直接调用已经声明且暴露的函数(直接输出传入的“module”)
// app.js部分
let btn = document.getElementById('btn');
btn.onclick = () => {
// 动态import
import('./module01.js').then(module => {
module.hello();
3.声明暴露函数
// module.js部分
export function hello() {
alert('Hello!!!!!!');
//动态引入import()变量失效问题
//webpack的现在的实现方式不能实现完全动态,所以可以通过字符串模板来提供部分信息给webpack
const modelpath = '/demo';
import(`@/pages${modelpath}`).then(item => {})
ES11,BigInt类型
BinInt是一种内置对象,它提供了一种方法表示大于2的53次方-1的整数。这原本是Javascript中可以用Number表示的最大数字。BigInt可以表示任意大的整数>>>>>
// 在一个整数字面量后面加“n”的方式定义一个BigInt
let num = 10n;
console.log(num); //10n
console.log(typeof num); //bigint
// 不支持浮点类型的数字
let num = 10.1n;
console.log(num); //Invalid or unexpected token
// 函数形式
let num = BigInt(10);
console.log(num); //10n
console.log(typeof num); //bigint
大数值运算---对比Javascript最大安全整数计算Number.MAX_SAFEE_INTEGER
// 超出Number.MAX_SAFE_INtEGER会出现问题
let maxNum = Number.MAX_SAFE_INTEGER;
console.log(maxNum); //9007199254740991
console.log(maxNum + 1); //9007199254740992
console.log(maxNum + 2); //9007199254740992
console.log(maxNum + 3); //9007199254740993
console.log(maxNum + 4); //9007199254740996
// 用BigInt
let maxNum = Number.MAX_SAFE_INTEGER;
console.log(BigInt(maxNum)); //9007199254740991n
console.log(BigInt(maxNum) + BigInt(1)); //9007199254740992n
console.log(BigInt(maxNum) + BigInt(2)); //9007199254740993n
console.log(BigInt(maxNum) + BigInt(3)); //9007199254740994n
console.log(BigInt(maxNum) + BigInt(4)); //9007199254740995n
>>>>>>由于在Number与BigInt之间进行转换会损失精度,因此建议仅在值大于2的53次方时shi用BigInt类型,并且不在这两种类型之间进行想换转换
ES11,globalThis
globalThis包含全局的this值,类似于全局对象global object>>>>>
在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过
window
、
self
或者
frames
取到全局对象,但是在
Web Workers
中,只有
self
可以。在 Node.js 中,它们都无法获取,必须使用
global
。
在松散模式下,可以在函数中返回
this
来获取全局对象,但是在严格模式和模块环境下,
this
会返回
undefined
。 You can also use
Function('return this')()
, but environments that disable
eval()
, like
CSP
in browsers, prevent use of
Function
in this way.
globalThis
提供了一个标准的方式来获取不同环境下的全局
this
对象(也就是全局对象自身)。不像
window
或者
self
这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用
globalThis
,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的
this
就是
globalThis
。
>>>>>简言之,如果说想对全局对象进行操作,可以直接忽略环境直接使用globalThis,就可以指向全局对象
ES12, String.prototype.replaceAll--- replaceAll()
- >>>>> replaceAll()返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉,替换规则可以是字符串或者正则表达式。
- 而replace()只能是替换字符串中匹配到的第一个实例字符,而不能进行全局多项匹配替换,唯一的办法是通过正则表达式进行相关规则匹配替换。
// replaceAll() 方法
let str = 'Java是世界上最好的编程语言!Java爱了爱了!';
let transformStr = str.replaceAll('Java','JavaScript');
console.log(transformStr); //JavaScript是世界上最好的编程语言!JavaScript爱了爱了!
// replace() 方法
let str02 = 'Java是世界上最好的编程语言!Java爱了爱了!';
let transformStr02 = str.replace('Java','JavaScript');
console.log(transformStr02); //JavaScript是世界上最好的编程语言!Java爱了爱了!
//注意,replaceAll在使用正则表达式的时候,如果非全局匹配(/g),则replaceAll()会抛出一个异常
let str = 'Java是世界上最好的编程语言!Java爱了爱了!';
console.log(str.replaceAll(/Java/,'JavaScript')); //TypeError
ES12,Promise.any
- >>>>>当Promise列表中的任意一个promise成功resolve则返回第一个resolve的结果状态
- 如果所有的promise均reject,则抛出异常表示所有请求失败
Promise.any([
new Promise((resolve, reject) => setTimeout(reject, 500, 'Java是世界上最好的编程语言!')),
new Promise((resolve, reject) => setTimeout(resolve, 1000, 'JavaScript是世界上最好的编程语言!')),
new Promise((resolve, reject) => setTimeout(resolve, 2000, 'Php是世界上最好的编程语言!')),
.then(value => console.log(value)) // JavaScript是世界上最好的编程语言!
.catch (err => console.log(err));
//还有这种情况
Promise.any([
Promise.reject('Error 1'),
Promise.reject('Error 2'),
Promise.reject('Error 3')
.then(value => console.log(value))
.catch (err => console.log(err))
//输出AggregateError: All promises were rejected
Promise.any与Promise.race十分容易混淆,务必注意区分,Promise.race 一旦某个promise触发了resolve或者reject,就直接返回了该状态结果,并不在乎其成功或者失败
ES12,WeakRefs
- >>>>>使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)
- 当我们通过(const、let、var)创建一个变量时,垃圾收集器GC将永远不会从内存中删除该变量,只要它的引用仍然存在可访问。WeakRef对象包含对对象的弱引用。对对象的弱引用是不会阻止垃圾收集器GC恢复该对象的引用,则GC可以在任何时候删除它。
- WeakRefs在很多情况下都很有用,比如使用Map对象来实现具有很多需要大量内存的键值缓存,在这种情况下最方便的就是尽快释放键值对占用的内存。
- 目前,可以通过WeakMap()或者WeakSet()来使用WeakRefs
let map = new Map();
function doSomething(obj) {
return obj;
function useObject(obj) {
doSomething(obj);
let called = map.get(obj) || 0;
called++;
if (called > 1000) {
console.log('当前调用次数已经超过1000次了');
map.set(obj, called);
以上虽然可以实现我们的功能,但是会发生内存溢出,因为传递给doSomething函数的每个对象都永久保存在map中,并且不会被GC回收,因此我们可以使用WeakMap
因为是弱引用,所以WeakMap、WeakSet的键值对是不可枚举的
WeakSet和WeakMap相似,但是每个对象在WeakSet中的每个对象只可能出现一次,WeakSet中所有对象都是唯一的
let ws = new WeakSet();
let foo = {}
let bar = {}
ws.add(foo);
ws.add(bar);
ws.has(foo); //true
ws.has(bar); //true
ws.delete(foo); //删除foo对象
ws.has(foo); //false 已删除
ws.has(bar); //仍存在
WeakSet与Set相比有以下两个区别
WeakSet只能是对象集合,而不能是任何类型的任意值
WeakSet弱引用,集合中对象引用为弱引用,如果没有其他对WeakSet对象的引用,则会被GC回收
最后,WeakRef实例有一个方法deref,返回引用的原始对象,如果原始对象被回收,则返回undefined
let cache = new Map();
let setValue = (key, obj) => {
cache.set(key, new WeakRef(obj));
let getValue = (key) => {
const ref = cache.get(key);
if (ref) {
return ref.deref();
let fibonacciCached = (number) => {
let cached = getValue(number);
if (cached) return cached;
let sum = calculateFibonacci(number);
setValue(number, sum);
return sum;
对于缓存远程数据来说,这可能不是一个好主意,因为远程数据可能会不可预测地从内存中删除。在这种情况下,最好使用LRU之类的缓存>>>>>
ES12,逻辑运算符和赋值表达式
逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的
复合赋值运算符有:操作运算符:+= -= *= /= %= **=
位操作运算符:&= ^= |=
按位运算符:<<= >>= >>>=
//表达式:a op= b
//等同于:a = a op b
//表达式:a op= b
//等同于:a = a op (a = b)
//a ||= b
//等价于
//a = a || (a = b)
//a &&= b
//等价于
//a = a && (a = b)
//a ??= b
//等价于
//a = a ?? (a = b)
//为什么不再是跟以前的运算公式a = a op b一样呢,而是采用a = a op (a = b)。因为后者当且仅当a的值为false的时候才计算赋值,只有在必要的时候才执行分配,而前者的表达式总是执行赋值操作
//??=可用来补充/初始化缺失的属性
let school = [{
name: '清华大学',
subject: '/'
subject: '/other'
for (let schoolName of school) {
schoolName.name ??= '默认排名';
console.table(school)
//(index) title path
//0 "主会场" "/"
//1 "默认标题" "/other
&&=:当LHS值存在时,将RHS变量赋值给LHS
||=:当LHS值不存在时,将RHS变量赋值给LHS
??= :当LHS值为null或者undefined时,将RHS变量赋值给LHS >>>>>
ES12,数字分隔符
>>>>>数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
let money01 = 1_000_000_000;
//等价于
let money02 = 1000000000;
let totalFee01 = 1000.12_34;
//等价于
let totalFee02 = 1000.1234;
//同样支持在八进制数中使用
let num01 = 0o123_456
//等价于