JS继承(二)

在JS继承(一)中总结了继承的原型链继承、盗用构造函数继承以及组合继承三种继承方式,当然js中的继承方式不仅仅这三种方式,所以这次再总结一下其它继承模式;

原型式继承

原型式继承使用情况:你有一个对象,想在它的基础上再创建一个新对象。
通过Object.create()实现,这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象。

let person = {
	name: "Jackson",
	friends: ["Jimi","Van","Jhone"]
};
let anotherPerson = Object.create(person);
anotherPerson.friends.push("Rob");
console.log(person.friends)    //   "Jimi","Van","Jhone","Rob"
let yetAnotherPerson = Object.create(person);
yetAnotherPerson .friends.push("James");
console.log(person.friends)    //   "Jimi","Van","Jhone","Rob","James"

Object.create()的第二个参数与Object.defineProperties()的第二个参数一样:每个新增的双属性都通过各自的描述符来描述。以这种方式添加的属性会遮蔽原型对象上的同名属性

let person = {
	name: "Jackson",
	friends: ["Jimi","Van","Jhone"]
};

let anotherPerson = Object.create(person,{
	name: {
		value: "Greg"
	}
});
console.log(anotherPerson.name)    //  "Greg"

原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住,属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。

寄生式继承

寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。

function object(o){
	function F(){}
	F.prototypr = o;
	return new F();
}
function createAnother(original){
	let clone= object(original);
	clone,sayHi = function(){
		console.log("hi");	
	};
	return clone;
}

在这段代码中,createAnother()函数接收一个参数,就是新对象的基准对象。这个对象original会被传给object()函数,然后将返回的新对象赋值给clone。接着给clone对象添加一个新方法sayHi()。最后返回这个对象。可以像下面这样使用createAnother()函数:

let person = {
	name: "Jackson",
	friends: ["Jimi","Van","Jhone"]
};
let anotherPerson = createAnother(person);
anotherPerson.sayHi();    // "hi"

这个例子基于person 对象返回一个新对象。新返回的anotherPerson对象具有person的所有属性和方法,还有一个新方法加sayHi()。
寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。object()函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用。

注意: 通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。

寄生式组合继承

之前说到的组合继承其实也存在效率问题,最主要的效率问题就是父类构造函数始终会被调用两次:一次是在创建子类原型时调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了,我们再来回顾一下组合继承:

function SuperType(name){
	this.name = name;
	this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
	console.log(this.name);
}

function SubType(name,age){
	//继承属性
	SuperType.call(this,name);     // 第二次调用SuperType()z
	this.age = age;
}

//继承方法
SubType.prototype = new SuperType();     //  第一次调用SuperType()

SubType.prototype.sayAge = function(){
	console.log(this.age);
}

在上面的代码执行后,SubType.prototype上会有两个属性:name和colors。它们都是SuperType的实例属性,但现在成为了SubType的原型属性。在调用SubType构造函数时,也会调用SuperType构造函数,这一次会在新对象上创建实例属性name和colors。这两个实例属性会遮蔽原型上同名的属性。
这样的话就会有两组name和colors属性:一组在实例上;另一组在SubType的原型上。这就是调用两次SuperType构造函数的结果。
寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类型构造函数给子类型原型赋值,而是取得父类型原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的对象赋值给子类原型。寄生式组合继承的基本模式如下:

function inheritPrototype(subType,superType){
	let prototype = object(superType.prototype);
	prototype.constructor = subType;
	subType.prototype = prototype;
}

这个inheritPrototype()函数实现了寄生式组合继承的核心逻辑。这个函数接收两个参数:子类构造函数和父类构造函数。在这个函数内部,第一步是创建父类原型的一个副本。然后,给返回的prototype对象设置constructor属性,解决由于重写默认导致默认constructor丢失的问题,最后将新创建的对象赋值给子类型的原型。如下例所示,调用inheritPrototype()就可以实现前面例子中的子类型原型赋值:

function SuperType(name){
	this.name = name;
	this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
	console.log(this.name);
}
function SubType(name,age){
	//继承属性
	SuperType.call(this,name);     // 第二次调用SuperType()z
	this.age = age;
}

inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
	console.log(this.age);
}

这里只调用了一次SuperType构造函数,避免了SubType.prototype上不必要也用不到的属性,因此可以说这个例子效率更高。寄生式组合继承可以算是引用类型继承的最佳模式。
至于ES6中新增的类(class)就不在这里说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值