9.1、class扩展
9.1.1、类成员声明
在ES13之前,我们只能在构造函数里面声明类的成员,而不能像其他大多数语言一样在类的最外层作用域里面声明成员。不过ES13出来之后,这都不算什么事儿了。现在我们终于可以突破这个限制,写下面这样的代码了:
javascript
class Car {
color = 'blue';
age = 2;
}
const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2
9.1.2、私有属性和私有方法
ES13之前,我们是不可能给类定义私有成员的。所以一些程序员为了表示某个成员变量是一个私有属性,会给该属性名添加一个下划线(_)作为后缀。可是这只能作为程序员之间的君子之约来使用,因为这些变量其实还是可以被外界访问到的。不过在ES13中,我们只需要给我们的属性/方法添加一个hashtag(#)前缀,这个属性/方法就变成私有的了。当我们的属性变为私有后,任何外界对其的访问都会出错哦。
javascript
<script>
class Person {
//公有属性
name;
//私有属性 #属性名;
#age;
#weight;
//构造方法
constructor(name, age, weight) {
this.name = name;
this.#age = age;
this.#weight = weight;
}
// 内部创建一个方法,外部执行,可以进行访问
intro() {
console.log(this.name);
console.log(this.#age);
console.log(this.#weight);
}
#say(){
console.log('hello');
}
}
//实例化
const girl = new Person("晓红", 18, "45kg");
// console.log(girl,'girl');
//Person {name: '晓红', #age: 18, #weight: '45kg'}
//直接在外部访问,私有属性访问不了
// console.log(girl.name);//晓红
// console.log(girl.#age);//报错
// console.log(girl.#weight);//报错
//通过内部访问,可以的
girl.intro();
// girl.#say() //不可以访问
</script>
9.1.3、静态私有属性和私有方法
跟私有属性和方法一样,我们只需要给我们的静态属性/方法添加一个hashtag(#)前缀,这个静态属性/方法就变成私有的了。只能在类内部访问啦。
类的静态方法可以使用this关键字访问其他的私有或者公有静态成员,而在类的实例方法中则可以通过this.constructor来访问这些静态属性.
javascript
class Person {
static #count = 0;
static getCount() {
return this.#count;
}
constructor() {
this.constructor.#incrementCount();
}
static #incrementCount() {
this.#count++;
}
}
const person1 = new Person();
const person2 = new Person();
console.log(Person.getCount()); // 2
// 下面都会报错
console.log(Person.#count);
console.log(Person.#incrementCount);
9.1.4、判断是否有私有变量
前面我们说了,可以定义私有属性和方法,但是在外部是没办法直接访问的,那么我们怎么知道某对象是否具有某私有属性呢?
在ES13中,我们可以通过in操作符来判断对象是否具有某私有属性。
javascript
class Car {
#color;
hasColor() {
return #color in this;
}
}
const car = new Car();
console.log(car.hasColor()); // true
9.1.5、支持定义静态代码块
ES13允许在类中通过static关键字定义一系列静态代码块,这些代码块只会在类被创造的时候执行一次。
一个类可以定义任意多的静态代码块,这些代码块会和穿插在它们之间的静态成员变量一起按照定义的顺序在类初始化的时候执行一次。我们还可以使用super关键字来访问父类的属性。
javascript
class Vehicle {
static defaultColor = 'blue';
}
class Car extends Vehicle {
static colors = [];
static {
this.colors.push(super.defaultColor, 'red');
}
static {
this.colors.push('green');
}
console.log(Car.colors); ['blue', 'red', 'green']
}
9.2、Async Await扩展
await协程的一种方案
顶层await只能用在ES6模块,不能用在CommonJS模块。这是因为CommonjS模块的require()是同步加载,如果有顶层await,就没法处理加载了。
javascript
<script type="'module">
function ajax(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve("data-1111");
},2000);
})
}
let res =await ajax();
console.log(res)
</script>
9.3、Array和String扩展
at函数
我们可以使用at函数来索引元素,支持数组和字符串。
javascript
const arr = ['a', 'b', 'c', 'd'];
// 第一个元素
console.log(arr.at(0)); // a
// 倒数第一个元素
console.log(arr.at(-1)); // d
// 倒数第二个元素
console.log(arr.at(-2)); // c
const str = 'randy';
// 第一个元素
console.log(str.at(0)); // r
// 倒数第一个元素
console.log(str.at(-1)); // y
// 倒数第二个元素
console.log(str.at(-2)); // d
注意:传正数从前往后找,下标从0开始。传负数从后往前找,下标从-1开始。
9.4、RegExp扩展
支持返回开始和结束索引
简单来说这个新属性就是允许我们告诉RegExp在返回match对象的时候,给我们返回匹配到的子字符串的开始和结束索引。
ES13之前,我们只能获取正则表达式匹配到的子字符串的开始索引:
javascript
const str = 'sun and moon';
const regex = /and/;
const matchObj = regex.exec(str);
// [ 'and', index: 4, input: 'sun and moon', groups: undefined ]
console.log(matchObj);
ES13后,我们就可以给正则表达式添加一个d的标记来让它在匹配的时候给我们既返回匹配到的子字符串的起始位置还返回其结束位置:
javascript
const str = 'sun and moon';
const regex = /and/d;
const matchObj = regex.exec(str);
/**
[
'and',
index: 4,
input: 'sun and moon',
groups: undefined,
indices: [ [ 4, 7 ], groups: undefined ]
]
*/
console.log(matchObj);
你看,设置完d标记后,多了一个indices的数组,里面就是匹配到的子字符串的范围了!
9.5、Object扩展
Object.hasOwn()
Object.hasOwn()函数接收两个参数,一个是对象,一个是属性,如果这个对象本身就有这个属性的话,这个函数就会返回true,否则就返回false。
javascript
const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
console.log(Object.hasOwn(obj, 'color')); // true
console.log(Object.hasOwn(obj, 'name')); // false
好奇的小伙伴就会问了,不是有hasOwnProperty()可以判断某对象是否具有某属性吗?为什么还是要出一个这样的方法。
其实原因有两点,
hasOwnProperty()方法是Object原型上的方法,所以可以被覆盖,如果覆盖了就达不到我们想要的结果了。
如果我们创建了一个原型为null的对象(Object.create(null)),也会获取不到该方法而报错。
9.6、Error扩展
Error对象的cause属性
Error对象的cause属性
ES13后,Error对象多了一个cause属性来指明错误出现的原因。这个属性可以帮助我们为错误添加更多的上下文信息,从而帮助使用者们更好地定位错误。这个属性是我们在创建error对象时传进去的第二个参数对象的cause属性:
javascript
function userAction() {
try {
apiCallThatCanThrow();
} catch (err) {
throw new Error('New error message', { cause: '请求出错啦' });
}
}
try {
userAction();
} catch (err) {
console.log(err);
console.log(`Cause by: ${err.cause}`); // Cause by: 请求出错啦
}
10.1、Array扩展
10.1.1、findLast 数组支持倒序查找
在JS中,我们可以使用数组的find()函数来在数组中找到第一个满足某个条件的元素。同样地,我们还可以通过findIndex()函数来返回这个元素的位置。可是,无论是find()还是findIndex(),它们都是从数组的头部开始查找元素的,可是在某些情况下,我们可能有从数组后面开始查找某个元素的需要。
ES13出来后,我们终于有办法处理这种情况了,那就是使用新的findLast()和findLastIndex()函数。这两个函数都会从数组的末端开始寻找某个满足条件的元素
javascript
const letters = [
{ value: 'z' },
{ value: 'y' },
{ value: 'x' },
{ value: 'y' },
{ value: 'z' },
];
// 倒序查找
const found = letters.findLast((item) => item.value === 'y');
const foundIndex = letters.findLastIndex((item) => item.value === 'y');
console.log(found); // { value: 'y' }
console.log(foundIndex); // 3
10.1.2、toSorted
sort方法的复制版本,区别就是sort是修改原数组,而toSorted是返回新数组。
我们先来看看sort
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.sort();
console.log("原数组:", arr);
console.log("新数组:", newArr);
我们再来看看toSorted
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.toSorted();
console.log("原数组:", arr);
console.log("新数组:", newArr);
看出区别来了吧,新老数组不一样
10.1.3、toReversed
reverse方法的复制版本,toReversed不修改原数组,返回新数组
我们先来看看reverse
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.reverse();
console.log("原数组:", arr);
console.log("新数组:", newArr);
我们再来看看toReversed
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.toReversed();
console.log("原数组:", arr);
console.log("新数组:", newArr);
看出区别来了吧,新老数组不一样
10.1.4、toSpliced
toSpliced与splice区别就很大了。splice是截取原数组的数据,并返回截取出来的数据。toSpliced是对原数组的副本进行操作,然后返回被截取完后的新数组,并不会修改原数组。
我们先来看看splice
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.splice(1, 2);
console.log("原数组:", arr);
console.log("新数组:", newArr);
我们再来看看toSpliced
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.toSpliced(1, 2);
console.log("原数组:", arr);
console.log("新数组:", newArr);
看出区别了吧,toSpliced并不会影响原数组。返回的是截取后的数组。
10.1.5、with
通with有点类似我们通过[index]来修改数组,区别就是with不是修改原数组,而是返回整个新数组。
我们先来看看通过下标来修改数组的
javascript
const arr = [1, 3, 5, 2, 8];
arr[1] = 10;
console.log("原数组:", arr);
javascript
const arr = [1, 3, 5, 2, 8];
const newArr = arr.with(1, 10);
console.log("原数组:", arr);
console.log("新数组:", newArr);
10.2、WeakMap扩展
支持 Symbol 作为键
之前WeakMap是只支持对象作为键,现在还支持 Symbol 作为键
javascript
const weak = new WeakMap();
const key = Symbol("ref");
weak.set(key, "randy");
console.log(weak.get(key)); // randy