skip to content
天真笔录

继承的几种方式

/ 4 min read

原型链继承

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函数执行了两次

  1. Child.prototype = new Person()
  2. 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();
  • 解决了父类引用数据污染
  • 解决了父类构造函数执行两次

    这也是目前最常用的继承方式