function Parent() {
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child() {
this.type = 'child2';
}
Child.prototype = new Parent()
console.log(new Child())
创建子类实例时,不能向父构造函数传递参数,导致继承的父类属性没有值。如果包含引用类型,会被所有的实例对象共享,容易造成修改混乱
const s1 = new Child()
const s2 = new Child()
s1.play.push(4)
console.log(s1.play, s2.play) // [1, 2, 3, 4]
function Parent(){
this.name = "parent"
}
Parent.prototype.getName = function(){
return this.name
}
function Child(){
Parent.call(this)
this.type = "Child"
}
let child = new Child()
console.log(child)
console.log(child.getName())
function Parent() {
this.name = "parent"
this.play = [1, 2, 3]
}
Parent.prototype.getName = function () {
return this.name
}
function Child() {
// 第一次调用父类
Parent.call(this)
this.type = "child"
}
// 第二次调用父类
Child.prototype = new Parent()
Child.prototype.constructor = Child
const child1 = new Child()
const child2 = new Child()
child1.play.push(4)
console.log(child1)
console.log(child2)
console.log(child1.getName())
组合继承解决了构造函数继承和原型链继承的缺点,但是却调用了两次父类,造成了额外的性能开销。
function clone (parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
function Child6() {
Parent6.call(this);
this.friends = 'child5';
}
clone(Parent6, Child6);
Child6.prototype.getFriends = function () {
return this.friends;
}
let person6 = new Child6();
console.log(person6); //{friends:"child5",name:"child5",play:[1,2,3],__proto__:Parent6}
console.log(person6.getName()); // parent6
console.log(person6.getFriends()); // child5
es6 extends
es6的extends原理就是基于寄生组合继承实现的。
原型继承
原型继承就是基于已有的对象来创建新的对象。实现原理就是向函数中传入一个对象,然后返回一个以这个对象为原型的对象。缺点与原型链继承相同:只能继承父构造函数的原型对象上的成员,不能继承父构造函数的实例对象的成员。
使用Object.create方法实现继承。
let parent4 = {
name: "parent4",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
let person4 = Object.create(parent4);
person4.name = "tom";
person4.friends.push("jerry");
let person5 = Object.create(parent4);
person5.friends.push("lucy");
console.log(person4.name); // tom
console.log(person4.name === person4.getName()); // true
console.log(person5.name); // parent4
console.log(person4.friends); // ["p1", "p2", "p3","jerry","lucy"]
console.log(person5.friends); // ["p1", "p2", "p3","jerry","lucy"]
因为Object.create实现的是浅拷贝,所以对于引用类型,存在修改数据混乱的问题。
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()); // parent5
console.log(person5.getFriends()); // ["p1", "p2", "p3"]
ES5的继承是通过prototype和构造函数机制来实现。ES5的继承是先创建子类的实例对象,然后再将父类的方法添加到this上。
function parent(a, b) {
this.a = a;
this.b = b;
}
function child(c) {
this.c = c
};
parent.call(child, 1, 2)
// 使用call绑定其实是实现了如下代码:
// child.prototype = new Parent(1, 2)
console.log(child);
ES6的继承机制是先创建父类的实例对象this,然后再调用子类的构造函数修改this。
class Parent {
constructor(a, b) {
this.a = a
this.b = b
}
}
class child extends Parent {
constructor(a, b, c) {
// super(a, b)
this.c = c
}
}
const c = new child(1, 2, 3)
console.log(c);
可以看到:ES5的继承原理是先创建子类元素child的实例对象,然后再把父类元素parent的原型对象
中的属性赋值给子类元素child的实例对象里面,从而实现继承;ES6引入了class的概念,父类首先实例化出来,再修改子类构造函数中的this实现继承。