导读
javascript面向对象编程学习(一)
javascript面向对象编程学习(二) —— 原型链继承
一、借用构造函数继承
1、 如何实现借用构造函数继承?
利用call()
或apply()
方法,在子构造函数Child()
中,调用Person.call(this, name)
,增强子构造函数实例;实质等同于复制父实例给子函数。
1.1代码实现
function Person(name) {
this.name = name;
this.names = ['大魔王','魔鬼', '恶魔'];
this.getName = function () {
console.log(this.name)
}
}
Person.prototype.say = function (){
console.log('my name is ' + this.name);
}
function Child(name, age){
// 继承Person
Person.call(this, name);
this.age = age;
}
var child = new Child('小魔王', 22);
var child1 = new Child('大魔王', 25);
console.log(child.names); // ["大魔王", "魔鬼", "恶魔"]
console.log(child1.names); // ["大魔王", "魔鬼", "恶魔"]
child.names.push('小魔王');
console.log(child.names); // ["大魔王", "魔鬼", "恶魔", "小魔王"]
console.log(child1.names); // ["大魔王", "魔鬼", "恶魔"]
console.log(child.name); // 小魔王
console.log(child.age); // 22
2、为什么可以用call()
或apply()
方法来实现继承?
首先,我们要了解一下call的定义:调用一个对象的一个方法,以另一个对象替换当前对象。
也就是说,当执行Person.call(this, name)
的时候,实质就是Person
的this
指向了Child
。
因此,实例child
也有了Person
的属性和方法了,也就实现了继承!
3、关系图如下:

4、缺点:
- 只能继承父类的实例属性和方法,不能继承原型属性/方法;
Person
的this
指向了Child
, 也就是说,Child
只是复制了Person
的属性和方法,原型链并没有和它产生关系;child
实例的__proto__
还是指向Child
。如下:- 无法实现构造函数的复用,每个子类都有父类实例函数的副本,影响性能,代码会臃肿。
二、 组合继承
1、什么是组合继承?
把原型链继承和构造函数继承的优点组合起来。使用构造函数继承实现对实例上的属性和和方法继承,使用原型链继承实现对原型上的属性和方法的继承。这样就能使在原型上定义的方法能够实现函数复用,并且没个实例也有自己的属性。
2、代码实现
function Person(name) {
this.name = name;
this.names = ['大魔王','魔鬼', '恶魔'];
}
Person.prototype.sayName = function (){
console.log('my name is:' + this.name);
}
function Child(name, age){
// 继承Person实例属性
Person.call(this,name);
this.age = age;
}
// 继承原型的属性和方法
Child.prototype = new Person();
Child.prototype.constructor = Child; // 因为原型链继承,会把constructor指向改变,所以要重新指回自身
Child.prototype.sayAge = function (){
console.log('my age is:' + this.age);
}
const child = new Child('小魔王', 22);
child.names.push('人');
child.sayName(); // my name is:小魔王
child.sayAge(); // my age is:22
console.log(child.names); // ["大魔王", "魔鬼", "恶魔", "人"]
const child2 = new Child('人', 28);
child2.sayName(); // my name is:人
child2.sayAge(); // my age is:28
console.log(child2.names); // ["大魔王", "魔鬼", "恶魔"]
3、关系图如下:

4、缺点
父类实例的属性和方法既存在于子类的实例中也存在于原型中。如下图:

三、 总结
想要弄清楚构造函数继承,要先弄明白
call()
和apply()
方法的定义和使用,要清楚this指向。
组合继承,当你弄清楚原型链继承和构造函数继承,然后再把两者结合去思考,就自然而然懂了。
学习的最好的办法还是实践;就譬如我这样动起手来,画图和敲代码等等,比只看而不去动手,要了解得更加透彻!
你的赞是对我最大的支持,也是我最大的动力!