JavaScipt之继承与原型链

继承是面向对象中一个核心的慨念。其他正统面向对象语言会有两种方式实现继承;一个是接口继承,一个是原型继承。而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。

function Box(){      //Box 构造
	this.name = 'wu';
}

function Desk(){     //Desk 构造
	this.age = 100;
}

Desk.prototype = new Box(); //Desk继承了Box,通过原型,形成链条

var desk = new Desk();
alert(desk.age);
alert(desk.name);   //得到被继承的属性

function Table(){
	this.level = 'AAAAA';
}

Table.prototype = new Desk();  //继续原型链继承

var table = new Table();

alert(table.name);    //继承了Box和Desk

原型链继承流程图

如果要实例化table,那么Desk实例中有age=100,原型中增加相同的属性age=200,最后结果是多少呢?

Desk.prototype.age = 200;  //实例与原型中均包含age

以上原型链继承还缺少一环,那就是Obejct,所有的构造函数都继承自Obejct。而继承Object是自动完成的,并不需要程序员手动继承。

经过继承后的实例,他们的从属关系会怎样呢?

alert(table instanceof Object);					//true
alert(desk instanceof Table);					//false,desk是table的超类
alert(table instanceof Desk);					//true
alert(table instanceof Box);					//true

JavaScript里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。继承也有之前问题,比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

为了解决引用共享和超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。

function Box(age){
	this.name = ['wu', 'Jet', 'Hello'];
	this.age = age;
}

function Desk(age){
	Box.call(this,age);  //对象冒充,给超类型传参
}

var desk = new Desk(200);
alert(desk.age);
alert(desk.name);
desk.name.push('AAA');
alert(desk.name);

借用构造函数虽然解决了刚才两种问题,但没有原型,复用则无从谈起。所以,我们需要原型链+借用构造函数的模式,这种模式成为组合继承

function Box(age){
	this.name = ['wu', 'Jet', 'Hello'];
	this.age = age;
}


Box.prototype.run = function(){
	return this.name + this.age;
};

function Desk(age){
	Box.call(this, age);
}

Desk.prototype = new Box();      	//原型链继承

var desk = new Desk(100);     

alert(desk.run());

还有一种继承模式叫做:原型式继承;这种继承借助原型并基于已有的对象创建新对象,同时还不必因此创建自定义类型。

function obj(o){		//传递一个字面量函数
	function F(){}		//创建一个构造函数
	F.prototype = o;	//把字面量函数赋值给构造函数的原型
	return new F();		//最终返回出实例化的构造函数
}


var box = {
	name:'wu',
	arr:['哥哥','妹妹', '姐姐']
};

var box1 = obj(box);
alert(box1.name);
box1.name = 'Jack';
alert(box1.name);

alert(box1.arr);
box1.arr.push('父母');
alert(box1.arr);

var box2 = new obj(box);
alert(box2.name);
alert(box2.arr);    //引用类型共享了

寄生式继承把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。

function create(o){		//封装创建过程
	var f = obj(o);
	f.run = function(){
		return this.arr;  //同样会共享引用
	};
	return f;
}

组合式继承是JavaScript最常用的继承模式;但,组合式继承也有一点小问题,就是超类型在使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。


function Box(name) {
	this.name = name;
	this.arr = ['哥哥','妹妹','父母'];
}

Box.prototype.run = function () {
	return this.name;
};

function Desk(name, age) {
	Box.call(this, name);					//第二次调用Box
	this.age = age;
}

Desk.prototype = new Box();					//第一次调用Box

以上代码是之前的组合继承,那么寄生组合继承,解决了两次调用的问题。

function obj(o) {
	function F() {}
	F.prototype = o;
	return new F();
}

function create(box, desk) {
	var f = obj(box.prototype);
	f.constructor = desk;
	desk.prototype = f;
}

function Box(name) {
	this.name = name;
	this.arr = ['哥哥','妹妹','父母'];
}

Box.prototype.run = function () {
	return this.name;
};

function Desk(name, age) {
	Box.call(this, name);
	this.age = age;
}

inPrototype(Box, Desk);						//通过这里实现继承

var desk = new Desk('Lee',100);
desk.arr.push('姐姐');
alert(desk.arr);
alert(desk.run());							//只共享了方法

var desk2 = new Desk('Jack', 200);
alert(desk2.arr);							//引用问题解决


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值