1、类字段声明
ES13之前,类字段只能在构造函数中声明,与许多其他语言不同,我们不能在类的最外层范围内声明或定义它们
<script>
class Car {
constructor(){
this.color = 'pink';
this.age = 18;
}
}
const car = new Car();
console.log(car.color); //pink
console.log(car.age); // 18
</script>
ES13消除了这个限制,现在我们可以编写如下代码:
<script>
class Cars {
color = 'black';
age = 16;
}
const car1 = new Cars();
console.log(car1.color); //black
console.log(car1.age); // 16
</script>
2、私有方法和字段
以前不能在类中声明私有成员,成员通常以下划线(_)为前缀,表示它是私有的,但仍然可以从类外部访问和修改
<script>
class Person {
_firstName = '小白';
_lastName = '小黑';
get name() {
return `${this._firstName} ${this._lastName}`;
}
}
const person = new Person();
console.log(person.name); //小白 小黑
console.log(person._firstName); // 小白
console.log(person._lastName); // 小黑
person._firstName = '小王';
person._lastName = '小明';
console.log(person.name); //小王 小明
</script>
使用ES13 我们现在可以将私有字段和成员添加到类中,方法是在其前面加上#号,试图从类外部访问它们会导致错误:
<script>
class Persons {
#firstName = "小刘";
#lastName = "小李";
get name() {
return `${this.#firstName} ${this.#lastName}`;
}
}
const persons = new Persons();
console.log(persons.name);
//这里抛出的错误是语法错误,发送在编译时,因此没有部分代码运行,编译器甚至不希望您尝试从类外部访问私有字段,因此,它假定您正在尝试声明一个
console.log(persons.#firstName);
console.log(persons.#lastName);
</script>
3、 await 运算符
在js中,await 运算符用于暂停执行,直到Promise 被解决(履行或拒绝)以前只能在async函数中使用此运算符 使用async关键字声明的函数
<script>
function setTimeoutAsync(timeout){
return new Promise((resolve) => {
setTimeout(()=> {
resolve();
},timeout)
})
}
// await is only valid in async functions and the top level bodies
// await setTimeoutAsync(3000);
</script>
使用ES13可以
<script>
function setTimeoutAsync(timeout){
return new Promise((resolve) => {
setTimeout(()=> {
resolve();
},timeout)
})
}
// waits for timeout - no error thrown
// await setTimeoutAsync(3000);
</script>
4、静态类字段和静态私有方法
在ES13中为类声明静态字段和静态私有方法,静态方法可以使用this关键字访问类中的其它私有/公共静态成员,实列方法可以使用this.constructor访问它们
<script>
class Personn {
static #count = 0;
static getCount(){
return this.#count
}
constructor(){
this.constructor.#inc()
}
static #inc(){
this.#count++;
}
}
const person1 = new Personn();
const person2 = new Personn();
console.log(Personn.getCount()); //2
</script>
5、类静态块
ES13允许在创建类定义只执行一次的静态块,这类似于其他面向对象编程的语言中的静态构造函数一个类的类主体中可以有任意数量的静态{} 初始化块,它们将于任何交错的静态字段初始值设定一起按照声明的顺序执行,可以在静态块中使用超属性来访问超类的属性
<script>
class Vehicle {
static defautColors = 'black';
}
class Card extends Vehicle {
static colors = [];
static {
this.colors.push(super.defautColors,'red')
}
static {
this.colors.push('green');
}
}
console.log(Card.colors); //["black" "red" "green"]
</script>
6、at( ) 方法进行索引
通常在js中使用方括号([])来访问数组的第N个元素,这通常是一个简单的过程,只访问数组的N-1 属性
<script>
let arr = ['a','b','c']
console.log(arr[1]); //b
</script>
但是如果想使用方括号访问数组末尾的第N个元素,我们必须使用arr.length-N 的索引
<script>
const arr1 = [1,2,3,]
console.log(arr1[arr1.length-1]); //3
console.log(arr1[arr1.length-2]); //2
</script>
新的at()方法更简洁,要访问数组末尾的第N个元素,只需要将负值-N 传递给 at()
<script>
let num = [3,4,5,6]
console.log(num.at(-1)); //6
console.log(num.at(-2)); //5
</script>
除了数组,字符串和TypedArray 对象现在也有 at()方法
<script>
const str = 'coding';
console.log(str.at(-1)); //g
console.log(str.at(-2)); //n
let typedArray = new Uint8Array([9,8,7,6]);
console.log(typedArray.at(-1)); //6
console.log(typedArray.at(-2)); //7
</script>
7、RegExp 匹配索引
新功能允许指定想要获取给定字符串中 RegExp对象匹配的开始和结束索引。以前只能在字符串中获取正则表达式匹配的起始索引
<script>
let strs = 'sun and moon';
let regex = /and/;
let matchObj = regex.exec(strs);
//['and',index:4,input:'sun and moon', groups:undefined]
console.log('matchObj',matchObj);
</script>
现在可以指定一个 d 正则表达式标志来获取匹配开始和结束的两个索引
<script>
const str1 = "sun and moon";
const regex1 = /and/d;
const matchObj1 = regex1.exec(str1)
// ['and',index:4,input:'sun and moon', groups:undefined, indices:[ groups:undefined, [4,7]]]
console.log('matchObj1',matchObj1);
</script>
8、Object.hasOwn( ) 方法
在js中,可以使用Object.prototype.hasOwnProperty()方法来检查对象是否具有给定的属性
<script>
class Car2 {
color = "white";
age = 14;
}
const car2 = new Car2();
console.log(car2.hasOwnProperty('age')); // true
console.log(car2.hasOwnProperty('name')); // false
</script>
但是这种方法存在一定问题,一方面Object.prototype.hasOwnProperty()方法不受保护,它可以通过为类定义自定义 hasOwnProperty()方法覆盖,该方法可能具有Object.prototype.hasOwnProperty()完全不同的行为
<script>
class Car3 {
color = "hotpink";
age = 11;
hasOwnProperty() {
return false
}
}
const car3 = new Car3();
console.log(car3.hasOwnProperty('age')); // false
console.log(car3.hasOwnProperty('name')); //false
//另一个问题是,对于使用Null 原型创建的对象(使用 Object.create(null)) 尝试对其调用此方法会导致错误
const obj = Object.create(null);
obj.color ="blue";
obj.age = 2;
// obj.hasOwnProperty is not a function
// console.log(obj.hasOwnProperty('color'));
</script>
解决上面问题的一种方法是使用调用 Object.prototype.hasOwnProperty Function 属性上的call()方法,如下:
const obj1= Object.create(null);
obj1.color ="blue";
obj1.age = 2;
obj1.hasOwnProperty = () => false;
console.log(Object.prototype.hasOwnProperty.call(obj1,'color')); // true
console.log(Object.prototype.hasOwnProperty.call(obj1,'name')); // false
这不是很方便,我们可以编写一个可重用的函数来避免重复自己
function objHasOwnProp(obj,propertyKey) {
return Object.prototype.hasOwnProperty.call(obj,propertyKey);
}
const obj2 = Object.create(null);
obj2.color = 'red';
obj2.age = 3;
obj2.hasOwnProperty = () => false;
console.log(objHasOwnProp(obj2,'color')); // true
console.log(objHasOwnProp(obj2,'name')); //fasle
不过没有必要,因为可以使用新的内置 Object.hasOwn()方法,与我们的可重用函数一样,它接受对象和属性作为参数 如果指定的属性是对象的直接属性,则返回 true 否则 返回false
const obj3 = Object.create(null);
obj3.color = 'orange';
obj3.age = 6;
obj3.hasOwnProperty = () => false;
console.log(Object.hasOwn(obj3,'color'),'obj3'); //true
console.log(Object.hasOwn(obj3,'name')); // false
9、错误原因
错误对象现在有一个 cause 属性,用于指定导致即将抛出的原始错误,这有助于为错误加额外的上下文信息并帮助诊断意外行为, 我们可以通过在作为第二个参数传递给 Error()构造函数的对象上设置 cause 属性来指定错误的原因
<script>
function userAction(){
try{
apiCallThatCanThrow();
}catch(err) {
throw new Error('New error message', { cause: err});
}
}
try{
userAction();
}catch(err) {
console.log('err',err);
console.log(`Cause by: ${err.cause}`);
}
</script>
10、从最后一个数组查找
在js中可以使用 Array find()方法在数组中查找通过指定测试条件的元素,同样也可以使用findIndex()方法来查找此类元素的索引
虽然 find()方法和findIndex() 都从数组的第一个元素开始搜索,但在某些情况下,最好从最后一个元素开始搜索.
在某些情况下,我们知道从最后一个元素中查找可能会获得更好的性能,列如,试图在数组中获取值prop等于 y 的项目.使用find()和findIndex();
<script>
const letters = [
{value:'v'},
{value:'w'},
{value:'x'},
{value:'y'},
{value:'z'},
]
const found = letters.find(item => item.value === 'y');
const foundIndex = letters.findIndex(v => v.value === 'y')
console.log('found',found); //{value: 'y'}
console.log('foundIndex',foundIndex); //3
</script>
但是由于目标对象更靠近数组的尾部,如果我们使用findLast()和findLastIndex()方法从末尾搜索数组,则可以让程序运行得更快
<script>
const letter = [
{value:'v'},
{value:'w'},
{value:'x'},
{value:'y'},
{value:'z'},
]
const founds = letter.findLast(item => item.value === 'y');
const foundsIndex = letter.findLastIndex(item => item.value === 'y');
console.log('founds',founds); //{value: 'y'}
console.log('foundsIndex',foundsIndex); //3
</script>
另一个用例可能要求我们专门从末尾搜索数组以获取正确的项目.例如,如果想在数字列表中查找最后一个偶数 find() 和 findIndex() 会产生错误的结果
<script>
let nums = [5,6,7,8,9,10];
let lastEven = nums.find(v => v % 2 === 0);
let lastEvenIndex = nums.findIndex(item => item % 2 === 0);
console.log('lastEven',lastEven); //6
console.log('lastEvenIndex',lastEvenIndex); //1
</script>
可以在调用find() 和findIndex()之前调用数组的reverse() 方法来反转元素的顺序.
但是这种方法会导致数组发生不必要的突变,因为reverse()会反转数组的元素,避免这种突变的唯一方法是
制作整个数组的新副本,这可能会导致大型数组出现性能问题.此外,findIndex()仍然无法在反转数组上工作,
因为反转元素也意味着更改他们在原始数组中的索引,要获得原始索引,需要执行额外的计算,意味编写更多的代码
<script>
let nums1 = [5,6,7,8,9,10];
let reversed = [...nums1.reverse()];
let lastEven1 = reversed.find(v => v % 2 === 0);
let reversedIndex = reversed.findIndex(v => v % 2 === 0);
let lastEvenIndex1 = reversed.length - 1 - reversedIndex;
console.log('lastEven1',lastEven1); //10
console.log('reversedIndex',reversedIndex); //0
console.log('lastEvenIndex1',lastEvenIndex1); // 5
let lastEven2 = nums1.findLast(v => v % 2 === 0);
let lastEvenIndex2 = nums1.findLastIndex(v => v % 2 === 0);
console.log('lastEven2',lastEven2); //6
console.log('lastEvenIndex2',lastEvenIndex2); //4
</script>