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()来创建想要的对象,这样可以直接知道我们创建的对象类型是XXX。JavaScript同样提供了一种方法,构造函数模式,如下:
function Person (name)
{
this.name = name;
this.show = function() { alert(this.name); };
}
在JavaScript中,任何函数只要通过new来调用,就可以成为构造函数,所以我们这样调用:
ver obj = new Person (“Jake”);
这样,我们就生成了一个Person对象,并设置了其name和show属性,这不仅和其他面向对象语言生成对象的方式相同,更清晰的告诉我们生成的对象的类型。采用这种方式需要注意的是,如果不使用new来调用该函数,那它就作为普通函数而非构造函数,如:
function(“jake”);
由于function是在全局环境中调用,this代表的就是window对象,因此,调用此函数实际上就是给window对象增加了name和show属性,所以采用构造函数时应该避免这种错误。
在C++中,类的属性在各个对象实例中都有一份副本存在,它们各不相同,而类的方法则是所有对象实例共享的,它们在调用时通过this指针进行区别。因此,我们也希望在JavaScript中有如下行为:
var p1 = new Person(“Jake”);
var p2 = new Person(“Ross”);
alert(p1.show == p2.show);
我们希望alert输出的应该是true,但是事实并非如此,p1和p2都有各自的数据和函数属性,它们各不相同,即便函数属性没必要生成多份。有一个很容易向导的方法就是将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中,这样所有实例都共享这个属性,相当于将属性和方法放到父类中,如果子类没有改写这个属性,那么就有一个默认的值。
转载于:https://blog.51cto.com/jslmes/1212933