日期:2014-05-16  浏览次数:20368 次

TDD测试驱动的javascript开发(3) ------ javascript的继承

说起面向对象,人们就会想到继承,常见的继承分为2种:接口继承和实现继承。接口继承只继承方法签名,实现继承则继承实际的方法。

由于函数没有签名,在ECMAScript中无法实现接口继承,只支持实现继承。

1. 原型链

1.1 原型链将作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

构造函数---原型---实例 之间的关系:

每一个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。

function SuperType() {
	this.property = true;
}

SuperType.prototype.getSuperValue = function() {
	return this.property;
};

function SubType() {
	this.subproperty = false;
}

SubType.prototype = new SuperType();    //通过原型链实现继承

SubType.prototype.getSubValue = function() {
	return this.subproperty;
};

var subInstance = new SubType();

var superInstance = new SuperType();

TestCase("test extends",{
	
	"test superInstance property should be true" : function() {
		assertEquals(true,superInstance.property);
	},
	"test superInstance getSuperValue() should be return true" : function() {
		assertEquals(true,superInstance.getSuperValue());
	},
	"test subInstance property should be false" : function() {
		assertEquals(false,subInstance.subproperty);
	},
	"test subInstance could visit super method " : function() {
		assertEquals(true,subInstance.getSuperValue());    //SubType继承SuperType,并调用父类的方法
	}
});

注:要区分开父类和子类的属性名称,否则子类的属性将会覆盖父类的同名属性值:看如下代码:


function SubType() {
	this.property = false;
}
SubType.prototype.getSubValue = function() {
	return this.property;
};
function SuperType() {
	this.property = true;
}

SuperType.prototype.getSuperValue = function() {
	return this.property;
};

SubType.prototype = new SuperType();    //通过原型链实现继承


var subInstance = new SubType();

var superInstance = new SuperType();

TestCase("test extends",{
	
	"test superInstance property should be true" : function() {
		assertEquals(true,superInstance.property);    //父类的property值为true
	},
	"test superInstance getSuperValue() should be return true" : function() {
		assertEquals(true,superInstance.getSuperValue());   //superInstance调用方法
	},
	"test subInstance property should be false" : function() {
		assertEquals(false,subInstance.property);     //子类的property属值为false
	},
	"test subInstance could visit super method " : function() {
		assertEquals(false,subInstance.getSuperValue());    //SubType继承SuperType,并调用父类的方法,可以属性被覆盖了,返回false
	}
});
续:当然,如果我们不要求对属性值进行初始化的时候,就不必考虑这个问题,我们会采用上一章讲的构造函数模式+原型模式来创建类和实现继承关系。

当以读取模式访问一个实例属性时,首先会在实例中搜索该属性,如果没有找到该属性,则继续在实例的原型中寻找。在通过原型链实现继承的情况下,会继续沿着原型链继续向上

subExtends.getSuperValue()
首先在实例中查找,然后在SubType.prototype,最后在SuperType.prototype中找到。

补充: 所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype,这也正是所有自定义类型都会继承toString()、valueOf()等默认方法的原因。

1.2 确定原型和实例的关系

 方法一:使用instanceof 操作符     ----     只要该实例是原型链中出现过的构造函数,结果就会返回true

function SuperType() {
	this.property = true;
}

SuperType.prototype.getSuperValue = function() {
	return this.property;
};

function SubType() {
	this.subproperty = false;
}

SubType.prototype = new SuperType();    //通过原型链实现继承

SubType.prototype.getSubValue = function() {
	return this.subproperty;
}