JavaScript是一种面向对象的语言,但是和其他如C++Java这些面向对象语言又有不同,在JavaScript中没有类的概念,对象则是指多个属性的无序组合,这些属性包括基本值、对象和函数。可以将JavaScript的对象看成是一个map类型,包含一系列的名值对,名称就是属性的名称,值可以是数据或函数。

我们可以想下面那样创建一个对象:

var obj = new Object();

obj.name = “Jake”;

obj.show = function(){ alert(this.name);};

以上通过new生成了一个Object对象引用,obj就相当于一个指针,指向了堆内存中的一个对象实例,然后通过给obj添加属性、方法,就有了一个我们想要的对象实例。由于JavaScript中的对象相当于是名值对,所以我们还可以这样创建对象:

var obj = { name:”Jake’, show:function(){alert(this.name);}; };

这样创建的对象和对一种方法创建的对象效果是一样的。这种方法虽然简单,但是有一个确定,即如果我们需要多个这样的对象,那么就需要多次调用new Object()然后对各个对象实例进行属性复制,这样的就造成了代码的冗余,也容易出错,所以我们需要一种方法来封装这种对象的生成操作。首先,可以写一个函数来封装对象的生成,如下:

function createObject(name)

{

var obj = new Object();

obj.name = name;

obj.show = function() { alert(this.name); };

return obj;

}

通过上面的函数,就可以在需要对象实例的地方调用createObject来获得一个是想,这类似于设计模式中的工厂模式,即将对象的生成操作封装起来。如果需要创建其他对象,同样可以写一个函数createXXX来创建对象XXX的实例,但是这种写法和其他面向对象语言中不同,也不符合习惯,我们想要通过new XXX()来创建想要的对象,这样可以直接知道我们创建的对象类型是XXXJavaScript同样提供了一种方法,构造函数模式,如下:

function Person (name)

{

this.name = name;

this.show = function() { alert(this.name); };

}

JavaScript中,任何函数只要通过new来调用,就可以成为构造函数,所以我们这样调用:

ver obj = new Person (“Jake”);

这样,我们就生成了一个Person对象,并设置了其nameshow属性,这不仅和其他面向对象语言生成对象的方式相同,更清晰的告诉我们生成的对象的类型。采用这种方式需要注意的是,如果不使用new来调用该函数,那它就作为普通函数而非构造函数,如:

function(“jake”);

由于function是在全局环境中调用,this代表的就是window对象,因此,调用此函数实际上就是给window对象增加了nameshow属性,所以采用构造函数时应该避免这种错误。

C++中,类的属性在各个对象实例中都有一份副本存在,它们各不相同,而类的方法则是所有对象实例共享的,它们在调用时通过this指针进行区别。因此,我们也希望在JavaScript中有如下行为:

var p1 = new Person(“Jake”);

var p2 = new Person(“Ross”);

alert(p1.show == p2.show);

我们希望alert输出的应该是true,但是事实并非如此,p1p2都有各自的数据和函数属性,它们各不相同,即便函数属性没必要生成多份。有一个很容易向导的方法就是将show函数从Person中提取出来,如下:

function show()

{

alert(this.name);

}

function Person(name)

{

this.name = name;

this.show = show;

}

这样Person的每个对象实例就都共享同一个show函数了。有一个问题是,这样做就是将show函数放到全局作用域中了,它不再属于Person这个“类”了,不符合对象的封装特性。那么怎样做才能使Person具有show方法,而Person的实例都共享它呢?JavaScript为我们提供了原型方法prototype。我们创建的每个函数都有一个prototype属性,它是一个对象,包含可以由特定类型的所有实例共享的属性和方法。可以这样理解,prototype就是通过构造函数创建的对象实例的原型对象,可以理解为父类,包含所有子类共有的属性和方法。因此我们可以这样写:

function Person(name)

{

this.name = name;

}

Person.prototype.show = function() { alert(this.name); };

通过这种原型模式定义的对象,就很符合C++中的类的概念,所有实例都保存一份数据成员,但共享同一份函数成员。实际上,我们可以将name属性放到prototype中,这样所有实例都共享这个属性,相当于将属性和方法放到父类中,如果子类没有改写这个属性,那么就有一个默认的值。