JS继承的实现
JS中继承是一个老生常谈的问题,但是依然还是有很多朋友可能不是很明白。本文就详细剖析ES6之前JS继承的常见实现方式。
ES6的继不在本文讨论范围内,有兴趣的朋友可以去看看阮一峰的:Class继承
既然要实现继承,那么首先创建一个父类1
2
3
4
5
6
7
8
9
10
11
12
13
14// 定义一个动物类
function Animal(name) {
// 实例属性
this.name = name || 'Animal'
// 实例方法
this.sleep = function() {
console.log(this.name + '正在睡觉!')
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃' + food)
}
好了,父类创建好了。现在我们就来用一下5中方式实现继承,同时说说每种实现方式的优缺点。
1. 原型链继承
核心:将父类的实例作为子类的原型对象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
31function Cat(color) {
// 实例属性
this.color = color || 'Black'
// 实例方法
this.catch = function(arg) {
console.log(this.name + '正在抓' + arg)
}
}
// Cat的原型对象指向Animal实例
// 这样Cat的原型对象上就有了Animal实例的属性和方法,从而实现继承
Cat.prototype = new Animal()
// 先继承后再添加Cat自己原型属性或方法
// 可以命名相同的名字以覆盖继承的属性或方法
Cat.prototype.name = 'cat'
Cat.prototype.climb = function(arg) {
console.log('快看!快看!颜色为' + this.color + '的' + this.name + '正在爬' + arg)
}
// ==============================TEST=================================================
var cat = new Cat('RED')
// 继承的name属性被自定义的name属性替代
console.log(cat.name) // cat
// 继承的方法
console.log(cat.sleep()) // cat正在睡觉
console.log(cat.eat('fish')) // cat正在吃fish
// Cat 实例属性和方法
console.log(cat.color) // RED
console.log(cat.catch('mouse')) // cat正在抓mouse
console.log(cat.climb('tree')) // 快看!快看!颜色为RED的cat正在爬tree
console.log(cat instanceof Animal) //true
console.log(cat instanceof Cat) //true
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
- 要想为子类新增原型属性和方法,必须要在new Animal()这个语句之后执行
- 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的
- 创建子类实例时,无法向父类构造函数传参
推荐指数:🔥🔥 .
2. Call(Apply)继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性/方法给子类(没用到原型)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
29function Cat(color, name) {
// Animal当做普通函数调用call方法传入this
Animal.call(this, name)
// 实例属性
this.color = color || 'Black'
// 实例方法
this.catch = function(arg) {
console.log(this.name + '正在抓' + arg)
}
}
Cat.prototype.climb = function(arg) {
console.log('快看!快看!颜色为' + this.color + '的' + this.name + '正在爬' + arg)
}
// ==============================TEST=================================================
var cat = new Cat('RED', 'cat')
// 继承的name属性被自定义的name属性替代
console.log(cat.name) // cat
// 继承的方法
console.log(cat.sleep()) // cat正在睡觉
// 无法继承父类原型方法
console.log(cat.eat('fish')) // TypeError: cat.eat is not a function
// Cat 实例属性和方法
console.log(cat.color) // RED
console.log(cat.catch('mouse')) // cat正在抓mouse
console.log(cat.climb('tree')) // 快看!快看!颜色为RED的cat正在爬tree
console.log(cat instanceof Animal) // false
console.log(cat instanceof Cat) // true
特点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例属性/方法(包含引用属性)的副本,影响性能
推荐指数:🔥🔥.
3. 实例继承
核心:为父类实例添加新特性,作为子类实例返回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
30function Cat(color, name) {
// 创建父类实例
var instance = new Animal(name)
// 实例属性
instance.color = color || 'Black'
// 实例方法
instance.catch = function(arg) {
console.log(this.name + '正在抓' + arg)
}
// 构造函数中用return语句返回的用法请自行Google
return instance
}
Cat.prototype.climb = function(arg) {
console.log('快看!快看!颜色为' + this.color + '的' + this.name + '正在爬' + arg)
}
// ==============================TEST=================================================
var cat = new Cat('RED', 'cat')
// 继承的name属性被自定义的name属性替代
console.log(cat.name) // cat
// 继承的方法
console.log(cat.sleep()) // cat正在睡觉
console.log(cat.eat('fish')) // cat正在吃fish
// Cat 实例属性和方法
console.log(cat.color) // RED
console.log(cat.catch('mouse')) // cat正在抓mouse
console.log(cat.climb('tree')) // TypeError: cat.climb is not a function
console.log(cat instanceof Animal) // true
console.log(cat instanceof Cat) // false
特点:
- 不限制调用方式,不管是new Cat()还是Cat(),返回的对象具有相同的效果(有点工厂函数的味道)
- 创建子类实例时,可以向父类传递参数
- 实例是父类的实例,不只是子类的实例
- 无法继承子类原型属性/方法
- 不支持多重继承
推荐指数:🔥 .
4. 组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用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
31function Cat(color, name) {
// Animal当做普通函数调用call方法传入this
Animal.call(this, name)
// 实例属性
this.color = color || 'Black'
// 实例方法
this.catch = function(arg) {
console.log(this.name + '正在抓' + arg)
}
}
Cat.prototype = new Animal()
Cat.prototype.climb = function(arg) {
console.log('快看!快看!颜色为' + this.color + '的' + this.name + '正在爬' + arg)
}
// ==============================TEST=================================================
var cat = new Cat('RED', 'cat')
// 继承的name属性被自定义的name属性替代
console.log(cat.name) // cat
// 继承的方法
console.log(cat.sleep()) // cat正在睡觉
console.log(cat.eat('fish')) // cat正在吃fish
// Cat 实例属性和方法
console.log(cat.color) // RED
console.log(cat.catch('mouse')) // cat正在抓mouse
console.log(cat.climb('tree')) // 快看!快看!颜色为RED的cat正在爬tree
console.log(cat instanceof Animal) // true
console.log(cat instanceof Cat) // true
// cat的构造器
console.log(cat.constructor) // [Function: Animal]
特点:
- 可以继承父类实例属性/方法,也可以继承原型属性/方法
- 创建子类实例时,可以向父类传递参数
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 支持多重继承
- 函数可复用
- 调用了两次父类构造函数,生成了两份实例属性/方法(一份在cat实例上, 一份在cat 原型对象上)
- cat的constructor 指向的Animal而不是Cat
推荐指数:🔥🔥🔥🔥.
5. 寄生组合继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在第二次调用父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点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
34
35
36
37
38
39
40
41
42
43
44function Cat(color, name) {
// Animal当做普通函数调用call方法传入this
Animal.call(this, name)
// 实例属性
this.color = color || 'Black'
// 实例方法
this.catch = function(arg) {
console.log(this.name + '正在抓' + arg)
}
}
// 添加一个IIFE块来把下面的实现包裹在一起
// 不要这个IIFE也没什么影响
(function(Cat) {
// 创建一个没有实例的方法
var Super = function() {}
Super.prototype = Animal.prototype
//将实例作为子类的原型
Cat.prototype = new Super()
// 修复Cat构造器指向
Cat.prototype.constructor = Cat
// 修改Cat构造函数本身的原型链 默认是:Cat.__proto__ === Function.prototype
Object.setPrototypeOf(Cat, Animal) // 这样写也可以:Cat.__proto__ = Animal
// 自己的原型属性/方法
Cat.prototype.climb = function(arg) {
console.log('快看!快看!颜色为' + this.color + '的' + this.name + '正在爬' + arg)
}
})(Cat)
// ==============================TEST=================================================
var cat = new Cat('RED', 'cat')
// 继承的name属性被自定义的name属性替代
console.log(cat.name) // cat
// 继承的方法
console.log(cat.sleep()) // cat正在睡觉
console.log(cat.eat('fish')) // cat正在吃fish
// Cat 实例属性和方法
console.log(cat.color) // RED
console.log(cat.catch('mouse')) // cat正在抓mouse
console.log(cat.climb('tree')) // 快看!快看!颜色为RED的cat正在爬tree
console.log(cat instanceof Animal) // true
console.log(cat instanceof Cat) // true
// cat的构造器
console.log(cat.constructor) // [Function: Cat]
特点:
- 可以继承父类实例属性/方法,也可以继承原型属性/方法
- 创建子类实例时,可以向父类传递参数
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 支持多重继承
- 函数可复用
- cat的constructor 正确的指向了Cat
- 堪称完美的继承方式
推荐指数:🔥🔥🔥🔥🔥.
6. Object.create继承
1 |
|