关于ES6中Class的讲解(上)

2022-09-26 11:43:19 浏览数 (1)

Class 是 ES6 的新特性,可以用来定义一个类,实际上,class只是一种「语法糖」,它是构造函数的另一种写法。(什么是语法糖?是一种为避免编码出错和提高效率编码而生的语法层面的优雅解决方案,简单说就是,一种便携写法。)由于Class的语法实在太多了,考虑到文章的阅读体验和便于大家理解,我把Class的讲解分为了(上)和(下)两部分,以下是(上)半部分,希望对大家理解Class有帮助。

文章目录:

Class的常见语法(上)

请大家仔细阅读文章的代码块,尤其是注释部分

1. 用class创建一个实例对象

JavaScript通过构造函数来生成实例对象,ES6之前创建对象的方法:

代码语言:javascript复制
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.toString = function () {
  return `我叫${this.name},今年${this.age}岁了`;
};

const person = new Person('dapan', 99);
console.log(person);// Object: Person {name: 'dapan', age: 99}
console.log(person.toString());// 我叫dapan,今年99岁了

ES6之后引入了Class(类)这个概念,通过class关键字可以定义类。用class改写上边的代码:

代码语言:javascript复制
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  toString() {
    console.log(this);
    return `我叫${this.name},今年${this.age}了`;
  }
  showName() {
    return this.name;
  }
}

const person = new Person('Dapan', 99);
console.log(person);// Object: Person {name: 'dapan', age: 99}
console.log(person.toString());// 我叫dapan,今年99岁了
console.log(person.showName());// Dapan

Class里面的constructor方法也叫做构造方法,里面的this关键字代表实例对象(在这里为person),且注意用class定义toString方法的时候,前面不需要加function关键字,方法之间也不需要加逗号。

与ES5一样,类的数据类型仍然是函数,且类必须用new关键字创建,否则会报错:

代码语言:javascript复制
class Person{
  // ...
}

console.log(typeof Person)// function
//报错:
Person()// Uncaught TypeError: Class constructor Person cannot be invoked without 'new'

Person类里面定义的方法均定义在Person的原型(prototype)上面,以上面的代码为例,打印Person类的prototype:

所以,如果大家有JS原型的基础,就知道下边代码的原理:

代码语言:javascript复制
console.log(Person.prototype.toString === person.toString);// true

2. 关于constructor()

(1)一定会创建

constructor()方法是Class类的默认方法,通过new命令创建实例对象的时候自动调用该方法。一个类必须要定义constructor()方法,如果没有显示定义,会自动创建一个空的constructor()方法:

代码语言:javascript复制
class Person{
    // ...
}
// 等同于:
class Person{
  constructor(){}
}

(2)默认返回实例对象this

constructor()方法默认返回实例对象:this。以文章开头的代码为例,是person对象,这就是我们打印person,默认打印结果为我们自己创建的,带有name和age属性的对象的原因:

代码语言:javascript复制
console.log(person);// Object: Person {name: 'dapan', age: 99}

当然也可以人为修改constructor()的返回值为其他对象,比如:

代码语言:javascript复制
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    return (this['a'] = 1);// 给this对象加了一个键值对:a:1
  }
}
const person3 = new Person('test',99)
console.log(person3)// Object: Person {name: 'Dapan', age: 99, a: 1}

当给了constructor()方法的返回值加了一个默认的属性:“a:1”之后,创建的实例对象person3也发生了相应的变化。

(3)指向“类”本身

与ES5一样,Class中prototype上的constructor属性直接指向“类”本身:

代码语言:javascript复制
console.log(Person.prototype.constructor === Person);// true

所以我们可以用这样的方式再创建一个person2对象:

代码语言:javascript复制
const person2 = new person.constructor('大潘', 100);
console.log(person2);//Object: Person {name: '大潘', age: 100}

3. 关于Class创建的实例

类的属性和方法,除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上),仍然以文章开头的代码为例,见下图:

与 ES5 一样,类的所有实例共享一个原型对象:

代码语言:javascript复制
var person4 = new Person();
var person5 = new Person();

console.log(person4.__proto__ === person5.__proto__);// true

4. 实例属性的新写法

ES2022为Class类的实例属性又定义了一种新写法,使得实例属性可以在除constructor()方法里面,用this.xxx定义,还可以在类内部的最顶层定义:

代码语言:javascript复制
class Person {
  test = 123;// 在由Person类创建的实例对象上,定义了一个属性:test:123
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // ...
}
const person = new Person('Dapan', 99);
console.log(person);// Object: Person {test: 123, name: 'Dapan', age: 99}

这里,testtoStringshowName处于同一层级,且不需要在test前加上this。但是这有一点需要注意,因为使用这种方式定义的实例属性是固定的,并不像name和age属性,可以在创建实例对象的时候再传值过去。

5. getter和setter

在Class类的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为,如下:

代码语言:javascript复制
class Person {
  //...
  get prop() {
    return '你想要prop吗?不给嘻嘻';
  }
  set prop(value) {
    console.log(`你尝试修改prop为:${value}`);
  }
}
const person6 = new Person()

我们尝试获取和修改person6的prop属性:

6. 使用表达式定义类

和普通函数一样,可以使用表达式的形式来定义类:

代码语言:javascript复制
const PersonClass = class Person {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return Person.name;
  }
};

const person7 = new PersonClass('大潘');// 注意这里创建实例对象使用类的名字是:PersonClass
console.log(person7);// Object: Person {name: '大潘'}

上面代码中,这个类的名字叫Person,但是Person只能在Class内部使用,在Class外部只能用等号前边的PersonClass,使用Person创建实例对象会报错。如果在Class内部没有用到的话,可以省略这里的Person

代码语言:javascript复制
const PersonClass = class {
    // ...
}

我们知道,ES6里面的Class其实只是一个语法糖,它的本质和ES5中的构造函数一样,都是函数类型:

代码语言:javascript复制
class Person{
   // ...
};
console.log(typeof Person);// function 

之前有一期推文给大家讲了立即自执行函数,那么我们就可以使用定义类的方式,写出立即执行的Class:

代码语言:javascript复制
const student = new (class Person {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return Person.name;
  }
})('Dapan');// 这里使用了立即自执行函数的形式

console.log(student);// Object: Person {name: 'Dapan'}

以上是Dapan关于JS中Class(上)半部分的讲解,(下)半部分预计在7月11号上午发出来……

参考资料:

https://es6.ruanyifeng.com/#docs/class

https://juejin.cn/post/6844903745558429710

0 人点赞