JavaScript 构造函数与 class

类与继承

  • ES6之前:使用构造函数模拟类
  • ES6之后:使用 class 语法糖

构造函数

用构造函数创建类

JS中的构造函数是一种特殊的函数,用于创建对象。它们是通过使用new关键字调用的函数,并且在函数内部使用this关键字来引用新创建的对象。构造函数可以包含属性和方法,这些属性和方法可以在创建对象时被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 创建一个Person类
function Person(name, age) {
this.name = name
this.age = age
}
// 这里实例化对象
let person1 = new Person("xx", 10);
let person2 = new Person("yy", 20);
```

类的**共享的属性和方法**要绑定在构造函数的原型上

```javascript
Person.prototype.say = function(){
console.log('I can say')
};
Person.prototype.run = function() {
console.log('I can run')
};
// 这样写比较繁琐,因此,可以作如下改进
Person.prototype = {
// 注意:需要让constructor 重新指回构造函数,
// 因为这里我们是重写了 prototype,会覆盖原来的属性+方法
constructor: Person,
// 将方法作为对象原型的一个属性
say: function(){
console.log('I can say')
},
// 或者直接写方法
run(){
console.log('I can run')
}
}

不过,对于原型对象的构造函数的重新指向,它应该是不可枚举。因此,在这里的原型对象的重新指向,应该使用Object.defineProperty的方式

1
2
3
4
5
6
7
Object.defineProperty(Person.prototype, "constructor", { 
configurable: false,
enumerable: false,
writable: true,
value: Person
})
`

规范

  • 类名首字母大写
  • 原型上的私有方法,默认以下划线开始

属性继承

通过call改变this指向的方式,实现子类继承父类的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
function Father(arg1, arg2){ 
this.arg1 = arg1
this.arg2 = arg2
}
function Son(arg1, arg2){
// 为了让子构造函数继承自父构造函数,利用call()
// 调用父构造函数,传入参数
// 这里的 this是改变 父构造函数的 this指向,让它指向子构造函数
// 这样 父构造函数的 this.uname就等于 Son.uname 了
// 即,让子构造函数继承了父构造函数的 属性
// 对于属性的继承
Father.call(this, arg1, arg2)
}

方法继承

方法和属性是不同的继承,因为方法是复杂数据类型。需要在原型上操作,直接将父类的原型赋值给子类,同时注意改变原型的构造函数的指向

1
2
3
4
5
6
7
Son.prototype = Father.prototype
Son.prototype.constructor = Son
```

但是,对象的赋值只是**浅拷贝**,修改子类的原型对象,也会修改父类的原型对象。因此,可以将子类的原型对象指向父类的一个**实例对象**

按照原型链的理论,实例对象也拥有`Father`原型对象上的方法,并且`Father`的实例对象和原型对象不是同一个对象。这时,因为 `Son`的原型对象指向的是实例对象,因此`Son`也可以获得 `Father`的方法,并且修改`Son`的原型对象不会影响`Father`**的原型对象**,因为修改的并不是同一个对象最后,还是需要手动让`Son`指回原来的构造函数

Son.prototype = new Father()
Son.prototype.constructor = Son

1
2

也可以使用`Object`方法

Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor = Son;
// 或者
Son.prototype = Object.create(Father.prototype, {
constructor: {
value: Son,
enumerable: false,
writable: false,
configurable: false
}
})

1
2
3
4
5
6
7


## class

ES6引入了类的概念,这是一种更简洁、更易于阅读和理解的面向对象编程方式。类是一种特殊的函数,用于创建对象。与构造函数不同,类的定义中包含了构造函数以及其他方法和属性的定义。

### 使用`class`语法糖构建类

// 创建一个Person类
class Person {
// 构造器方法
constructor(name, age){
this.name = name // 构造器中的this–类的实例对象
this.age = age
}
// 共有方法
say(){
console.log(‘I can say’)
}
run(){
console.log(‘I can run’)
}
fun1() {
console.log(‘这个方法放在了原型对象上’)
}
fun2 = ()=> {
console.log(‘这个方法放在了实例自身上’)
}
static fun3 = ()=> {
console.log(‘我前面用static进行修饰, 我是加在类本身的方法上的方法’)
}
static fun4() {
console.log(‘我前面用static进行修饰, 我是加在类本身的方法上的方法’)
}
}

// 创建一个实例化对象,必须使用 new 关键词
let person1 = new Person(“xx”, 10);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

关于类的构造函数`constructor`

在类(class)中,constructor是一个特殊的方法,用于在创建对象时初始化实例属性和方法。它接受传递给类的参数,并使用它们来创建和初始化类的对象。constructor方法是类中默认的构造函数,constructor这个关键字是固定的,不能随意修改。

当一个实例化对象被创建时,它会自动调用constructor方法以初始化自己,用于实例化对象时初始化属性和方法。当使用new关键字创建类的实例时,构造函数会被自动调用。它是一个默认的方法,如果没有手动定义,则会自动在类中创建。

关于 方法和属性

* **通过赋值语句创建的方法,是放在实例自身上的,调用通过`实例对象名.方法名`**
* **直接声明的方法,是放在原型对象上的,调用通过`实例对象名.方法名`**
* **如果方法前面用`static`进行修饰,这个方法时放在类的本身上的,调用通过`类名.方法名`**


### 继承

* 通过`extends`关键字声明继承
* `extends`可以继承任何类型的表达式,包括**原生对象**
* 只要该表达式最终返回的是一个可继承的函数,即具有`[[constructor]]`
* 通过`super`在子类调用父类的构造函数
* 非必须,如果在子类缺省了构造函数`constructor`,则会默认调用`super`函数,并传入所有参数

class Father {
constructor(arg1, arg2){
}
}

// 使用 extends 关键字,表示继承自父类
class Son extends Father {
constructor(arg1, arg2, arg3){
// 必须先调用父类的构造方法,再使用子类的构造方法
super(arg1, arg2) // 相当于调用了父类的构造函数来赋值,这样才可以使用父类的方法
this.arg3 = arg3 // 如果子类有自己的属性,则应该在super之后
}
}


关于继承中的方法

1.  继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
2.  继承中,如果子类里面没有就去查找父类有没有这个方法,如果有,就执行父类的这个方法(遵循就近原则)
3.  但可以通过`super`主动调用父类里面的方法:`super.方法()`


## JS的构造函数和ES6的类之间的区别

JS的构造函数和ES6的类之间有几个重要的区别。以下是一些最重要的区别:

*   语法:构造函数使用函数声明的方式来定义,而类使用class关键字来定义。
*   继承:类支持更简单和更灵活的继承方式,同时也支持多重继承。构造函数也可以实现继承,但是需要使用原型链来实现。
*   可读性:类的语法更加易于阅读和理解,因为它使用了更加自然的面向对象编程方式。
*   实例化:构造函数需要使用new关键字来创建新的实例,而类可以直接调用构造函数来创建新的实例。

JavaScript 构造函数与 class
https://flepeng.github.io/021-frontend-02-JavaScript-JavaScript-构造函数与-class/
作者
Lepeng
发布于
2024年3月11日
许可协议