「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」
JS进阶系列文章
👇 阅读本文你将学习到 👇
- 类中的构造函数
- 类的实例、静态、私有属性
- 类的实例、静态、私有方法
- 类的继承
Getter and Setter
- 关于
class
一些扩展知识点
在ES6
(ECMAScript6
)之前,JavaScript
语法中是不支持类的,导致面向对象编程方法无法直接使用,但我们可以通过function来实现模拟出类,而随着JavaScript
的更新,在ES6
出现了中出现class
关键字,可以用于定义类。接下来让我们看看它的如何使用的。
class
下面我们来看看如何使用class
关键字声明一个类。
1class Animal {
2
3}
4
5
6
7const Animal = class {
8
9}
而在ES6
之前,我们都是通过以下这样子的方式来模拟出类的。
1function Animal(){
2
3}
类的构造函数
每一个类都可以有一个自己的构造函数,这个名称是固定的constructor
,当我们通过new
调用一个类时,这个类就会调用自己的constructor
方法(构造函数)。
- 它用于创建对象时给类传递一些参数
- 每一个类只能有一个构造函数,否则报错
通过new
调用一个类时,会调用构造函数,执行如下操作过程:
- 在内存中开辟一块新的空间用于创建新的对象
- 这个对象内部的
__proto__
属性会被赋值为该类的prototype
属性 - 构造函数内的this,指向创建出来的新对象
- 执行构造函数的内部代码
- 如果函数没有返回对象,则返回
this
1class Animal {
2
3
4 constructor(name) {
5 this.name = name;
6 }
7}
8
9var a = new Animal("ABC");
10console.log(a);
上面这个例子中,我们在class
中定义的constructor
,这个就是构造方法,而this
代表的是实例对象。
这个class
,你可以把它看作构造函数的另外一种写法,因为它和它的构造函数的相等的,即是类本身指向构造函数。
1console.log(Animal === Animal.prototype.constructor);
其实,在类上的所有方法都会放在prototype
属性上。
类中的属性
实例属性
实例的属性必须定义在类的方法里,就如上面的例子,我们在构造函数中定义name
这个属性。
1class Animal{
2 constructor(name,height,weight) {
3 this.name = name;
4 this.height = height
5 this.weight = weight
6 }
7}
静态属性
当我们把一个属性赋值给类本身,而不是赋值给它prototype
,这样子的属性被称之为静态属性(static
)。
静态属性直接通过类来访问,无需在实例中访问。
1class Foo{
2 static name ='_island'
3}
4
5console.log(Foo.name);
私有属性
私有属性只能在类中读取、写入,不能通过外部引用私有字段。
1class Animal{
2 #age;
3 constructor(name,age){
4 this.name=name
5 this.#age=age
6 }
7}
8
9var a = new Animal('_island',18)
10console.log(a);
11console.log(a.name);
12console.log(a.age);
13console.log(a.#age);
我们通过getOwnPropertyDescriptors
方法获取到它的属性,同样也是获取不到。
1console.log(Object.getOwnPropertyDescriptors(a))
2
3{
4 name: {
5 value: '_island',
6 writable: true,
7 enumerable: true,
8 configurable: true
9 }
10}
私有字段仅能在字段声明中预先定义。
公共和私有字段声明是JavaScript标准委员会TC39提出的实验性功能(第3阶段)。浏览器中的支持是有限的,但是可以通过Babel等系统构建后使用此功能。
类中的方法
实例方法
在ES6
之前,我们定义类中的方法是类中的原型上进行定义的,防止类中的方法重复在多个对象上。
1function Animal() {}
2Animal.prototype.eating = function () {
3 console.log(this.name + " eating");
4};
在ES6
中,定义类中的方法更加简洁,直接在类中定义即可,这样子的写法即优雅可读性也强。
1class Animal{
2 eating() {
3 console.log(this.name + " eating");
4 }
5}
静态方法
静态方法与上面提到的静态属性是一样的,在方法前面使用static
关键字进行声明,之后调用这个方法时不需要通过类的实例来调用,可以直接通过类名来调用它。
1class Animal{
2 static createName(name) {
3 return name
4 }
5}
6
7var a2 = Animal.createName("_island");
8console.log(a2);
私有方法
在面向对象中,私有方法是一个常见需求,但是在ES6中没有提供,我们可以通过某个方法来实现它。
1class Foo {
2 __getBloodType() {
3 return "O";
4 }
5}
6
需要注意的是,通过下划线开头通常我们会局限它是一个私有方法,但是在类的外部还是可以正常调用到这个方法的
类的继承
extends
关键字用于扩展子类,创建一个类作为另外一个类的一个子类。
它会将父类中的属性和方法一起继承到子类的,减少子类中重复的业务代码。
这对比之前在ES5
中修改原型链实现继承的方法的可读性要强很多,而且写法很简洁。
extends的使用
1class Animal{
2
3}
4
5
6class dog extends Animal {
7
8}
继承类的属性和方法
下面这个例子,我们定义了dog
这个类,通过extends
关键字继承了Animal
类的属性和方法。
在子类的constructor
方法中,我们使用了super
关键字,在子类中它是必须存在的,否则新建实例时会抛出异常。这是因为子类的this对象是继承自父类的this对象,如果不调用super
方法,子类就得不到this
对象。
1class Animal {
2 constructor(name) {
3 this.name = name;
4 }
5 eating() {
6 console.log(this.name + " eating");
7 }
8}
9
10
11class dog extends Animal {
12 constructor(name, legs) {
13 super(name);
14 this.legs = legs;
15 }
16 speaking() {
17 console.log(this.name + " speaking");
18 }
19}
20
21var d = new dog("tom", 4);
22d.eating();
23d.speaking();
24console.log(d.name);
Super
super关键字用于访问和调用一个对象的父对象上的函数。
super
指的是超级、顶级、父类的意思
在子类的构造函数中使用this
或者返回默认对象之前,必须先通过super
调用父类的构造函数。
下面这段代码,子类的constructor
方法中先调用了super
方法,它代表了父类的构造函数,也就是说我们把参数传递进去之后,其实它是调用了父类的构造函数。
1class Animal{
2 constructor(name)
3}
4
5class dog{
6 constructor(name,type,weight){
7 super(name)
8 this.type=type
9 this.weight=weight
10 }
11}
下面这段代码使用super调用父类的方法
1class Animal {
2 constructor(name) {
3 this.name = name;
4 }
5 eating() {
6 console.log(this.name + " eating");
7 }
8}
9
10
11class dog extends Animal {
12 constructor(name, legs) {
13 super(name);
14 this.legs = legs;
15 }
16 speaking() {
17 super.eating()
18 console.log(this.name + " speaking");
19 }
20
21}
22
23var d = new dog("tom",4);
24d.speaking();
Getter 和 Setter
在类内部也可以使用get
和set
关键字,对应某个属性设置存值和取值函数,拦截属性的存取行为。
1class Animal {
2 constructor() {
3 this._age = 3;
4 }
5
6 get age() {
7 return this._age;
8 }
9
10 set age(val) {
11 this._age = val;
12 }
13}
14
15var a = new Animal();
16console.log(a.age);
17a.age = 4;
18console.log(a.age);
关于class扩展
严格模式
在类和模块的内部,默认是严格模式,所以不需要使用use strict
指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。
name属性
ES6
中的类只是ES5
构造函数的一层包装,所以函数的许多属性都被class
继承了,包括name
属性。
1class Animal{
2
3}
4console.log(Animal.name);
变量提升
class
不存在变量提升,这与我们在ES5
中实现类的不同的,function
关键字会存在变量提升。
1new Foo();
2class Foo {}
总结
在ES6
之后,我们在定义类以及它内部的属性方法,还有继承操作的语法变得非常简洁且易懂,class
是一个语法糖,其内部还是通过ES5
中的语法来实现的。且有些浏览器不支持class
语法,我们可以通过babel
来进行转换。