js继承的几种方式

1. 原型链

方法

将原型对象的指针指向另一个类的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function SuperType() {
this.name = "SuperType";
}
SuperType.prototype.getSuperName = function() {
return this.name;
}

function SubType() {

}
SubType.prototype = new SuperType();

var sub = new SubType();
console.log(sub.getSuperName());

注意使用
  • 用原型链实现继承时,==不能使用对象字面量创建原型方法==,这样会重写原型链。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function SuperType() {
this.name = "SuperType";
}
SuperType.prototype.getSuperName = function() {
return this.name;
}

SubType.prototype = new SuperType();
function SubType() {
type : "SubType",
getSubName : function() {
return this.type;
}
}
  • 若子类要覆盖超类中的方法,或添加自己的方法,添加方法的代码要放在替换原型的语句之后。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function SuperType() {
this.name = "SuperType";
}
SuperType.prototype.getSuperName = function() {
return this.name;
}

function SubType() {

}
//原型链继承
SubType.prototype = new SuperType();

//添加方法需放在原型继承语句之后
SubType.prototype.getSubName = function() {

}
问题
  • 若包含引用类型,不可通过原型链的方式来实现继承。原型中定义的属性和方法会在所有实例中共享。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function SuperType() {
this.name = ["s", "u", "p", "e", "r"];
}
SuperType.prototype.getSuperName = function() {
return this.name;
}

function SubType() {

}
SubType.prototype = new SuperType();

var sub1 = new SubType();
sub1.name.push("sub");
console.log(sub1.getSuperName()); //[ 's', 'u', 'p', 'e', 'r', 'sub' ]

var sub2 = new SubType();
console.log(sub2.getSuperName()); //[ 's', 'u', 'p', 'e', 'r', 'sub' ]
  • 在创建子类实例时,不可向超类构造函数传递参数。

2. 借用构造函数

  • 改进
    当原型中包含引用类型时,借用构造函数每个实例都有其父类属性的副本,并不是共享所有属性。
1
2
3
4
5
6
7
8
9
10
11
function SuperType() {
this.name = ["s", "u", "p", "e", "r"];
}

function SubType() {
//调用超类构造函数实现继承
SuperType.call(this);
}

var sub = new SubType();
console.log(sub.name);

借用构造函数可向父类构造函数中传递参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function SuperType(type) {
this.name = ["s", "u", "p", "e", "r"];
this.type = type;
}

SuperType.prototype.getName = function() {
return this.name;
}

function SubType(type) {
SuperType.call(this, type);
}

SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;

var sub1 = new SubType("SubType");
console.log(sub1.getName());
  • 问题
    借用构造函数方式,方法都在构造函数中定义,无法实现函数复用

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
function SuperType(type) {
this.name = ["s", "u", "p", "e", "r"];
this.type = type;
}

SuperType.prototype.getType = function() {
return this.type;
}

function SubType(name) {
//继承共有属性
SuperType.call(this, name);
}

SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
//实例共用的方法
SubType.prototype.getName = function() {
return this.name;
}

var sub1 = new SubType("sub1");
sub1.name.push("sub");
console.log(sub1.getType());
console.log(sub1.getName());

var sub2 =new SubType("sub2");
console.log(sub2.getType());
console.log(sub2.getName());
  • 缺点

组合继承会调用两次超类构造函数。

4. 原型式继承

借助原型,基于已有对象创建新对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}

var person = {
name: "zhangsan",
array: [1, 2, 3]
};

var obj1 = new object(person);
obj1.array.push(4);
console.log(obj1.name); //zhangsan
console.log(obj1.array); //[ 1, 2, 3, 4 ]

var obj2 = new object(person);
console.log(obj2.name); //zhangsan
console.log(obj2.array); //[ 1, 2, 3, 4 ]

ECMAScript5 新增Object.create()方法规范原型式继承。

问题
  • 所有子类实例共享父类属性
  • 无法实现函数复用

5. 寄生式继承

创建一个仅用于封装继承过程的函数,在函数内部以某种方式增强对象,后返回该对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createObj(obj) {
var clone = Object.create(obj);
clone.greet = function() {
console.log("hi");
};
return clone;
}

var person = {
name: "zhangsan",
array: [1, 2, 3]
}

var obj = createObj(person);
  • 缺点

未实现函数复用

6. 寄生组合式继承

  • 通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。
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
function inherPrototype(subType, superType) {
var prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}

function SuperType(type) {
this.type = type;
this.name = ["s", "u", "p", "e", "r"];
}

SuperType.prototype.getType = function() {
return this.type;
}

function SubType(type, name) {
SuperType.call(this, type);

this.name = name;
}

inherPrototype(SubType, SuperType);

SubType.prototype.getName = function() {
return this.name;
}
  • 改进

仅调用一次超类构造函数。