原型链继承
function Person() {
this.name = "parentPerson";
this.friends = ["zhangsan", "lisi"];
}
function Child() {
this.age = 28;
}
const p = new Person();
Child.prototype = p;
const c1 = new Child();
console.log(c1.age, c1.name); // c1.name = parentPerson
实现继承的关键
Child.prototype = new Person()
缺点
c1.friends.push("zhaoliu");
console.log(c1.friends); // ["zhangsan", "lisi", "zhaoliu"]
const c2 = new Child();
console.log(c2.friends); // ["zhangsan", "lisi", "zhaoliu"]
对于引用数据类型,如果其中一个child修改了,也会影响到其他child
构造函数继承
function Person() {
this.name = "parentPerson";
this.friends = ["zhangsan", "lisi"];
}
Person.prototype.say = function () {
console.log(this.name + "--- friends:" + this.friends.toString());
};
function Child() {
Person.call(this);
this.age = 28;
}
const c1 = new Child();
c1.friends.push("zhaoliu");
console.log(c1.friends); // ["zhangsan", "lisi", "zhaoliu"]
const c2 = new Child();
console.log(c2.friends); // ["zhangsan", "lisi"]
缺点
构造函数继承解决了引用类型共享问题,但是它存在其他问题
c1.say()
Uncaught TypeError: c1.say is not a function // 无法使用父类原型中的属性和方法
组合继承(原型链+构造函数)
function Person() {
this.name = "parentPerson";
this.friends = ["zhangsan", "lisi"];
}
Person.prototype.say = function () {
console.log(this.name + "--- friends:" + this.friends.toString());
};
function Child() {
Person.call(this);
this.age = 28;
}
Child.prototype = new Person();
Child.prototype.constructor = Child;
const c1 = new Child();
c1.friends.push("zhaoliu");
console.log(c1.friends); // ["zhangsan", "lisi", "zhaoliu"]
const c2 = new Child();
console.log(c2.friends); // ["zhangsan", "lisi"]
c1.say();
c2.say();
缺点
组合继承解决了前两种存在的问题,但是它自己也存在问题,Person函数执行了两次
Child.prototype = new Person()
Person.call(this)
原型式继承
// 原型式继承
const Person = {
name: "parentValue",
friends: ["zhangsan", "lisi"],
say: function () {
console.log(this.name + "--- friends:" + this.friends.toString());
},
};
// 道格拉斯
// function createObj(o) {
// const Fn = function() {}
// Fn.prototype = o
// return new Fn()
// }
// ES6 setPrototypeOf
// function createObj(o) {
// const obj = {}
// Object.setPrototypeOf(obj, o)
// return obj
// }
// ES6 create
function createObj(o) {
return Object.create(o);
}
const child = createObj(Person);
child.name = "childValue";
child.say();
child.friends.push("zhaoliu"); // ["zhangsan", "lisi", "zhaoliu"]
const child2 = createObj2(Person);
console.log(child2.friends); // ["zhangsan", "lisi", "zhaoliu"]
缺点
原型式继承也存在共享引用数据问题
寄生式继承
// 寄生式继承
const Person = {
name: "parentValue",
friends: ["zhangsan", "lisi"],
say: function () {
console.log(this.name + "--- friends:" + this.friends.toString());
},
};
// ES6 create
function createObj(o) {
const obj = Object.create(o);
obj.getFriends = function () {
console.log(this.friends);
};
return obj;
}
const child = createObj(Person);
console.log(child.getFriends);
创建obj时,为obj添加属性和方法,这种方式就成为寄生式继承
寄生组合式继承
function Person() {
this.name = "parentPerson";
this.friends = ["zhangsan", "lisi"];
}
Person.prototype.say = function () {
console.log(this.name + "--- friends:" + this.friends.toString());
};
function Child() {
Person.call(this);
this.age = 28;
}
Child.prototype = Object.create(Person.prototype); // 与组合式继承只有这一行不同
Child.prototype.constructor = Child;
const c1 = new Child();
c1.friends.push("zhaoliu");
console.log(c1.friends); // ["zhangsan", "lisi", "zhaoliu"]
const c2 = new Child();
console.log(c2.friends); // ["zhangsan", "lisi"]
c1.say();
c2.say();
- 解决了父类引用数据污染
- 解决了父类构造函数执行两次
这也是目前最常用的继承方式