- 原型对象是类的唯一标识:当且仅当两个对象继承自同一个原型对象时,它们才是属于同一个类的实例。而初始化对象的状态的构造函数则不能作为类的标识,两个构造函数的prototype属性可能指向同一个原型对象。那么这两个构造函数创建的实例是属于同一个类的。
- 一个典型的面向对象的js程序:
functionRange(from,to){
this.from=from;
this.to=to;
}
Range.prototype={
includes:function(x){
return this.from<=x&&x<=this.to;
},
foreach:function(f){
for(varx=Math.ceil(this.from);x<this.to;x++){
f.log(x);
}
},
toString:function(){
return"("+this.from+"..."+this.to+")";
}
}
//用例
var r=new Range(1,3);
console.log(r.includes(2));
r.foreach(console);
console.log(r.toString());
3.代码规范:类(构造函数)首字母大写,(普通)方法首字母小写
4.实际上instanceof运算符并不会检查r是否是由Range()构造函数而来,而会检查是否继承自Range.prototype。不过,instanceof的语法则强化了“构造函数是类的公有标识”的概念。
5.constructor属性:实例,
varF=function(){
}
varp=F.prototype;//这是F相关联的原型对象
var c=p.constructor;//这是与原型相关联的函数
console.log(p);//函数的原型是对象Object{}
console.log(c);//p的构造函数是构造函数function(){}
可以看到构造函数的原型中存在预先定义好的constructor属性,这意味着对象通常继承的constructor均指代它们的构造函数。由于构造函数是“公共标识”,因此这个constructor属性为对象提供了类。
var o=new F(); //创建类F的一个对象
o.constructor===F;//true,constructor属性指代这个类
构造函数和原型对象之间的关系如下图:
需要注意的是2中定义的Range类使用它自身的一个对象重写预定义的Range.prototype对象。这个新定义的原型对象不含有constructor属性。如下图:
因此Range类的实例也不含有constructor属性。我们可以通过补救措施来修正这个问题,显示给原型添加一个构造函数。
Range.prototype={
constructor:Range,
includes:function(x){
returnthis.from<=x&&x<=this.to;
},
foreach:function(f){
for(varx=Math.ceil(this.from);x<this.to;x++){
f.log(x);
}
},
toString:function(){
return"("+this.from+"..."+this.to+")";
}
}
另一种常见的解决方法是使用预定义的原型对象,预定义的原型对象包含constructor属性,然后依次给原型对象添加方法
Range.prototype.includes=function(x){returnthis.from<=x&&x<=this.to;};
Range.prototype.foreach=function(f){for(var x=Math.ceil(this.from);x<this.to;x++){f.log(x);};
Range.prototype.toString=function(){return"("+this.from+"..."+this.to+")";};
6.Javascript中类继承:构造函数对象,任何添加到这个构造函数对象中的属性都是类字段和类方法(如果属性值是函数的话就是类方法);原型对象,原型对象的属性被类的所有实例所继承,如果原型对象的属性值是函数的话,这个函数就作为类的实例的方法来调用;实例对象,类的每个实例都是一个独立的对象,直接给这个实例定义的属性是不会为所有实例对象共享的。定义在实例上的非函数属性,实际上是实例的字段。
7.javascript中定义类的步骤可以缩减为三步:1)先定义一个构造函数,并设置初始化新对象的实例属性。2)给构造函数的prototy对象定义实例的方法;3)给构造函数定义类字段和类属性,下面是一段模拟java中类的定义的js代码:
/**
* 复数的构造函数,这个构造函数为它所创建的每个实例定义实例字段r和i
* @param {[type]} real [实部]
* @param {[type]} imaginary [虚部]
*/
functionComplex(real,imaginary){
if(isNaN(real)||isNaN(imaginary)){
throw new TypeError(‘传入参数类型错误!‘);
}
this.r=real;
this.i=imaginary;
}
/**
* 复数加法,类的实例方法定义为原型对象的函数值属性
* 这里定义的方法可以被所有实例继承,并为他们提供共享行为
* 需要注意的是,Javascript的实例化方法必须使用关键字this来存取实例的字段
* @param {[type]} c [description]
*/
Complex.prototype.add=function(c){
return new Complex(this.r+c.r,this.i+c.i);
}
//这里定义了一些复数运算有帮助的类字段
Complex.ZERO=new Complex(0,0);
8. 编码规范:1)以大写字母命名的成员是不能修改的;2)以下划线为前缀的命名在类外不可见
9.Javascript中基于原型的继承机制是动态的:对象从其原型继承属性,如果创建对象之后原型的属性发生改变,也会影响到继承这个原型的所有实例对象。