上文提到我们不会单独使用基于函数伪造的方式实现继承,而是会使用基于原型链和函数伪装组合的方式实现继承。这种继承方式也叫伪经典继承,它的思想是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数的复用,又能够保证每个实例都有它自己的属性。
来看下面的例子,我们先列出基于原型链和函数伪装组合的方式实现继承完整的代码,然后再对代码中的每一步做内存模型分析。
// 第一部分
function Parent(name){
this.color = ["red","blue"];
this.name = name;
}
Parent.prototype.talk = function(){
alert(this.name+"["+this.color+"]");
}
// 第二部分
function Child(name,age){
//函数伪造继承
Parent.call(this,name);
this.age = age;
}
// 原型链继承
Child.prototype = new Parent();
Child.prototype.say = function(){
alert(this.name+","+this.color);
}
//第三部分
var c1 = new Child("Leon",22);
c1.color.push("green");
c1.say(); // 输出:Leon[red,blue,green]
var c2 = new Child("Ada",25);
c2.say(); // 输出:Ada[red,blue]
我们先来看代码中的第一部分,在这段代码中,我们创建了父类Parent,并为它添加了2个属性。然后在Parent的原型中添加一个方法talk()
。此时的内存模型如下图所示:
接下来在代码的第二部分,我们创建了子类Child,在子类Child内部使用函数伪造的方式继承父类的属性。然后通过原型链继承的方式使子类的原型指向父类对象,并在新的子类原型上添加了一个say()
方法。此时的内存模型如下图所示:
最后,在第三部分代码中,我们分别创建了两个子类对象c1和c2。然后为c1对象的color
属性添加一个新的颜色,并调用c1的say()
方法,对于c2同样也调用它的say()
方法。此时的内存模型如下图所示:
我们可以看到,为对象的引用类型属性设置值是在它自己的空间中完成的,这样每一个对象都有它自己独立的属性,互不干扰。
以上就是基于原型链和函数伪装组合的方式实现继承的完整代码和内存模型分析,也是我们在JavaScript中最常用的一种实现继承的方式。