javaScrip中的类是基于其原型继承机制的,可以让每个对象共享某些属性。类的成员或实例都包含一些属性,用以存放或定义它们的状态,其中有些属性定义了它们的行为(方法)。
类和原型
类的所有实例对象都从同一个原型对象上继承属性。
任何类的方法都可以通过this的这种基本用法来读取对象的属性。
function inherit(p) {
if (p == null) throw TypeError();
if (Object.create) return Object.create(p);
var t = typeof p;
if (t != "object" && t != "function") throw TypeError();
function f() {}
f.prototype = p;
return new f();
}
function range(from, to) {
// //用 range.methods作为原型创建一个新对象
var r = inherit(range.methods);
r.from = from;
r.to = to;
return r;
}
//原型对象定义方法,这些方法为每个范围对象所继承。
range.methods = {
includs: function (x) {
return this.from <= x && this.to >= x;
},
foreach: function (f) {
for (var x = Math.ceil(this.from); x <= this.to; x++) {
f(x);
}
},
toString: function () {
return "(" + this.from + "..." + this.to + ")";
},
};
var r = range(1, 3);//创建一个r对象
alert(r.includs(2));//输出true
document.write(r.toString() + "<br/>");//输出{1...3}
类和构造函数
构造函数是用来初始化新创建的对象的,构造函数的prototype属性被用来做新对象的原型。
定义构造函数即定义类,类名首字母要大写,而普通函数和方法都是小写。
function Range(from,to) {
//这俩个属性是私有的不可继承的。
this.from=from;
this.to=to;
}
//构造函数的prototype是所有创建对象的原型,所用用Range1 new出来的对象的原型都是这个对象
Range1.prototype={
// 这个方法比较值是否在这个对象的范围之内 ,this指代的就是调用这个方法的对象。
includes:function (x) {
return this.from<x&&this.to>x;
},
//重写toString this 所代表的就是用new 创建出来的Range1的实例
toString:function () {return "("+this.from+"...."+this.to+")"
}
}
var r=new Range(1,3);//使用构造函数来创建一个对象
alert(r.includes(2)); //输出true
console.log(r.toString()); //输出{1...3}
构造函数和类的标识
原型对象是类的唯一标识:当且仅当两个对象继承自同一个原型对象时它们才属于同一个类的实例。两个构造函数的prototype属性指向同一个原型对象时,则两个构造函数创建的实例属于同一个类。通过r(实例对象) instanceof Range(类) 判断 r是否继承自Range.prototype,来检查r是否继承自Range.prototype。
constructor属性
“每个JavaScript函数”(ECMAScript 5中的Function.bind()方法返回的函数除外)都自动拥有一个prototype属性,都可以作为构造函数。这个属性是一个对象,这个对象包含唯一一个不可枚举的属性constructor。construtor属性的值是一个函数对象,它指代“每个JavaScript函数”的构造函数。
构造函数的原型可以使用 Range.prototype来获取。
实例对象的原型可以使用 r.constructor来获取。
var F=function(){};//这是一个函数对象
var p=F.prototype;//这是F相关联的原型对象
var c=p.constructor;//这是与原型相关联的函数
c==F//=>true:对于任意函数F.prototype.constructor==F
新定义的原型对象不含有construtor属性,可以通过两个方式来解决:
1.重写原生对象。
function Rang() {
}
Rang3prototype={
constructor:Rang3,
//新定义的对象不含有这个属性,必须显示的设置构造函数反向引用
toString:function () {
return "tostring";
}
}
2。使用预定义的原型对象,预定义的原型对象包含constructor对象,然后依次给原型对象添加方法。
Rang3.prototype.toString=function () {
return "";
}
Rang3.prototype.from=function () {
}
JavaScript中java式的类继承
JavaScript中的函数都是以值的形式出现的,方法和字段之间并没有太大的差别。JavaScript牵扯三种·不同的对象:
定义类的步骤可以缩减为三步
1.先定义一个构造函数,并设置初始化新对象的实例属性
2.给构造函数的prototype对象定义实例的方法
3.给构造函数定义类字段和类属性
var SimpleRange = defineClass(
//用以设置实例的属性的函数
function (f, t) {
this.f = f;
this.t = t;
},
//实例的方法,复制至原型中
{
includs: function (x) {
return this.f <= x && x <= this.t;
},
toString: function () {
return this.f + "..." + this.t;
},
},
//类属性,复制到构造函数中
{
upto: function (t) {
return new SimpleRange(o, t);
},
}
);
类的扩充
JavaScript中基于原型的继承机制是动态的:对象从其原型继承属性,如果创建对象之后原型的属性发生改变,也会影响到继承这个原型的所有实例对象。
可以给Object.prototype添加方法,从而使所有的对象都可以调用这些方法。当这种方法不推荐,因为在ECMAScript5之前,无法将这些新增的方法设置为不可枚举,如果给Object.prototype添加属性这些属性是可以被for/in循环遍历的。
类和类型
JavaScript语言核心中的内置对象(通常是客户端JavaScript的宿主对象)可以根据它的class属性来区分彼此。
1.instanceof运算符
左操作数是待检测其类的对象,右操作数是定义类的构造函数。如果o继承自c.prototype,则表达式o instanceof c值为true.
使用isPrototypeOf()方法可以检测对象的原型链上是否存在某个特定的原型对象,这不使用构造函数作为中介。
range.methos.isPrototype®;//range.methos 是原型对象
这两个方法的缺点是:无法通过对象来获取类名,只能检测对象是否属于指定的类名。
2.constructor属性
识别对象是否属于某个类可以使用constructer属性。因为构造函数是类的公共标识。
function typeAndValue(x){
if(x==null)return "";
switch(x.constructor){
case Number:return "Number: "+x;
case String:return "Stirng: "+x;
case Date:return "Stirng: "+x;
case RegExp:return "RegExp: "+x;
case Comple:return "Comple: "+x;
}
}
3.构造函数的名称
使用instanceof运算符和constructor属性检测对象所属的类有一个主要的问题,在多个执行上下文中存在构造函数的多个副本的时候,这两种方法的检测结果会出错。另外一种方式就是使用构造函数的名字而不是构造函数本身作为类标识符。
鸭式辨型
检测对象的类的各种技术多少都会有些问题,解决方法方法就是规避这些问题。不要关注“对象的类是什么”,而是关注“对象能做什么”。
子类
在面向对象编程中,类B可以继承自另外一个类A。我们称A为父类,B为子类。
定义子类
JavaScirpt的对象可以从类的原型对象中继承属性。首先要确保子类的原型对象继承自父类的原型对象。
B.prototype=inherit(A.prototype);//子类派生自父类(inherit()是例6-1)
B.prototype.constructor=B;//重载继承来的constructor属性
构造函数和方法链
在定义子类时,大多需要对父类的行为进行修改或扩充,而不是完全替换他们。为做到这点,构造函数和子类的方法需要调用或链接到父类的构造函数和父类方法。