JavaScript 继承

JS(以下都简称 JS)也有继承吗?有的。不过跟 Java 的继承有区别,与其说是继承,不如说是继承机制。

首先可以明确一点,JS 中的所有对象都是由 Object 对象继承而来,这点跟 Java 中的 java.lang.Object 相似;还有 JS 对于本地类和宿主类不能作为基类被继承,这点和 Java 中被 final 修饰的类相似,比如 String 、Array 等,不管是 Java 还是 JS 中都不能作为基类被继承。不同于 Java 的单继承,JS 对象可以实现多继承,下面就来展示 JS 的继承方式。

对象冒充(object masquerading)

原理:类声明的构造函数方式里,构造函数可以用 this 关键字给所有属性和方法赋值,因为构造函数只是一个函数,所以可以使 ClassA 的构造函数成为 ClassB 的方法,然后调用它,这样 ClassB 就会拥有 ClassA 的构造函数中定义的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script type="text/javascript">
function Animal(sName) {
this.sName = sName;
this.eat = function(){
document.write(sName + " is eating... <br />" )
}
}

function Panda(sName,sColor){
Animal.call(this,sName); // 这里是继承机制的关键
this.color = function(){
document.write("This is a " + sColor + " " + sName +" <br />");
}
}

var cat = new Animal("Cat");
cat.eat(); // 输出 Cat is eating...
var panda = new Panda("Panda","black")
panda.eat(); // 输出 Panda is eating...
panda.color(); // 输出 This is a black Panda
</script>

原型链(prototype chaining)

prototype: 对该对象的对象原型的引用。所有对象都有 prototype 属性,其默认返回的是 Object 对象的实例,利用此,来实现继承

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
<script type="text/javascript">
function Animal() {
}

Animal.prototype.sName = "";
Animal.prototype.eat = function(){
document.write(this.sName + " is eating... <br />" );
}

function Panda(){
}

Panda.prototype = new Animal; // 原型方式实现继承的关键

Panda.prototype.sColor = "";
Panda.prototype.color = function(){
document.write("This is a " + this.sColor + " " + this.sName +" <br />");
}

var cat = new Animal();
cat.sName = "Cat";
cat.eat(); // 输出 Cat is eating...
var panda = new Panda()
panda.sName = "Panda";
panda.sColor = "black"
panda.eat(); // 输出 Panda is eating...
panda.color(); // 输出 This is a black Panda

document.write("<br />");
document.write("panda is Animal : " + (panda instanceof Animal) + " <br />"); // 输出 true
document.write("panda is Panda : " + (panda instanceof Panda) + " <br />"); // 输出 true
</script>

此方式的弊端很明显,一是无法使用带参数的构造函数,二是不支持多继承。且如下代码

1
Panda.prototype = new Animal;

执行后,会将 Panda 之前赋值的所有方法都删除,所以 Panda 自己特有的属性和方法,都必须出现在 prototype 属性被赋值之后,为什么呢?因为 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。

上面代码的最后,加了两行类型判断的代码输出,因为 prototype 的关系,用 instanceof 运算符来判断,结果都为 true,这与 Java 又多了一分相似处。

混合方式

原型链方式的弊端很明显,而对象冒充的方式则必须使用构造函数方式,所以将两者结合就完美了

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

<script type="text/javascript">
function Animal(sName) {
this.sName = sName;
}

Animal.prototype.eat = function(){
document.write(this.sName + " is eating... <br />" );
}

function Panda(sName,sColor){
Animal.call(this,sName);
this.sColor = sColor;
}

Panda.prototype = new Animal()
Panda.prototype.color = function(){
document.write("This is a " + this.sColor + " " + this.sName +" <br />");
}

var cat = new Animal("Cat");
cat.eat(); // 输出 Cat is eating...
var panda = new Panda("Panda","white");
panda.eat(); // 输出 Panda is eating...
panda.color(); // 输出 This is a white Panda

document.write("<br />");
document.write("panda is Animal : " + (panda instanceof Animal) + " <br />"); // 输出 true
document.write("panda is Panda : " + (panda instanceof Panda) + " <br />"); // 输出 true
</script>

仅以此记录