一 :原型链继承
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child1() {
this.type = 'child1';
}
Child1.prototype = new Parent1();
console.log(new Child1());
var s1 = new Child1()
var s2 = new Child1()
s1.play.push(4)
console.log(s1.play, s2.play) // [1,2,3,4] [1,2,3,4]
但是有一个弊端就是,原型继承的对象只是一个引用,那么就是每个实例都可以修改,这就是使用原型链继承方式的一个缺点。
因为我们期望的是s2 = [1,2,3]
关于编程想法,为什么Child1.prototype = new Parent1()不放入Child1构造函数里面呢?
因为当我们new关键字作用的时候,通过 obj.__proto__ = Object.create(constructor.protype),这相当于拷贝对象,
那么下面执行这个constructor改变的原型已经意义不大了,因为指向的位置变了。
二 :借用构造函数继承( 借助 call/apply)
this.name = 'parent2';
}
Parent2.prototype.getName = function () {
return this.name;
}
function Child2(){
Parent2.call(this);
this.type = 'child2'
}
let child = new Child2();
console.log(child); // 没问题
console.log(child.getName()); // 会报错
除了 Child2 的属性 type 之外,也继承了 Parent2 的属性 name。这样写的时候子类虽然能够拿到父类的属性值,解决了第一种继承方式的弊端,但问题是,只能继承父类的实例属性和方法,不能继承原型属性或者方法。 上面的两种继承方式各有优缺点,那么结合二者的优点,于是就产生了下面这种组合继承的方式。
三 :组合式继承
function Parent3 (age) {
this.name = 'parent3';
this.play = [1, 2, 3];
this.age = age
}
Parent3.prototype.getName = function () {
return this.name;
}
function Child3() {
// 第二次调用 Parent3()
Parent3.call(this,30);
this.type = 'child3';
}
// 第一次调用 Parent3()
Child3.prototype = new Parent3(30);
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play); // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'
虽然这种方式弥补了上述两种方式的一些缺陷,但有些问题仍然存在:
1. 子类仍旧无法传递动态参数给父类!
1. 父类的构造函数被调用了两次。
四 : 原型式继承
利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。 这个方案很像原型链继承
function object(obj){
function F(){}
F.prototype = obj;
return new F();
}
缺点:
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
- 无法传递参数
另外,ES5中存在Object.create()的方法,能够代替上面的object方法。
五 : 寄生式继承
核心:在原型式继承的基础上,增强对象,返回构造函数
使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承。
虽然其优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,还是在父类基础上添加了更多的方法。那么我们看一下代码是怎么实现
let parent5 = {
name: "parent5",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
function clone(original) {
let clone = Object.create(original);
clone.getFriends = function() {
return this.friends;
};
return clone;
}
let person5 = clone(parent5);
console.log(person5.getName());
console.log(person5.getFriends());
六 : 寄生组合式继承
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
function Child6() {
Parent6.call(this);
this.friends = 'child6';
}
function clone (parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
clone(Parent6, Child6);
Child6.prototype.getFriends = function () {
return this.friends;
}
let person6 = new Child6();
console.log(person6);
console.log(person6.getName());
console.log(person6.getFriends());
// 它可以解决组合继承 父类被调用两次和在不同层级属性重复的问题。
- 子类继承了父类的属性和方法,同时,属性没有被创建在原型链上,因此多个子类不会共享同一个属性。
- 子类可以传递动态参数给父类!
- 父类的构造函数只执行了一次!