JavaScript 中没有类的概念,所以它的对象也与基于类的语言中的对象有所不同。创建一个JavaScript对象有如下的方式,由浅入深一 一列举对比。
- 基础模式
使用JavaScript中的Object来创建,如下代码:
var computer=new Object();
computer.color="White";
computer.brand="Apple";
computer.description=function(){
console.log(this.brand+","+this.color+" computer");
};
这种方式的缺点在于,如果创建很多对象,就需要有很多重复的代码,除了在测试及简单应用中使用上面的方式,基本很少使用了。
- 工厂模式
抽象出创建具体对象的过程,通过函数来间接生成对象,如下:
function createComputer(color,brand){ var o=new Object(); o.color=color; o.brand=brand; o.description=function(){ console.log(this.brand+","+this.color+" computer"); }; } var macbookpro=createComputer("White","Apple"); var thinkpad=createComputer("black","IBM");
使用这种设计模式解决了创建多个相似对象的问题,但由于是通过函数方式来生成对象,所以无法识别一个对象的类型。
- 构造函数类型
通过创建自定义的构造函数,在构造函数中定义对象的属性和方法,如下代码:
function Computer(color,brand){ this.color=color; this.brand=brand; this.description=function(){ console.log(this.brand+","+this.color+" computer"); }; } var macbookpro=new Computer("White","Apple"); var thinkpad=new Computer("black","IBM");
可以看到,创建实例的时候,使用了关键字new操作符,这种方式的缺点就是每一个方法均需要在创建每一个实例的时候再创建一次。当然,我们可以把方法放在构造函数外面,通过函数变量指向外部方法来避免这个问题,但这样就破坏了OO的封装性,同时,所有的外部方法函数,均变为了"全局函数”。
【注】在使用new创建一个新的实例的时候,经过四个步骤。创建一个新对象;将构造函数的作用域给新对象(此时this就会指向这个新对象);执行构造函数中的代码(为新对象添加属性);返回新对象.
- 原型模式
JavaScript中的每一个函数都有一个prototype属性,它可以包含由特定类型的所有实例共享的属性与方法,即生成一个让所有对象实例共享属性方法的原型对象类型。如下代码:
function Computer(){}; Computer.prototype.color="White"; Computer.prototype.brand="Apple"; Computer.prototype.description=function(){ console.log(this.brand+","+this.color+" computer"); }; var macbookpro1=new Computer(); var macbookpro2=new Computer();
即macbookpro1与macbookpro2两个变量访问同一组属性与方法。当然,上面的代码可以使用字面变量的方式重新写一下
function Computer(){}; Computer.prototype={ constructor: Computer, color:"White", brand:"Apple", description:function(){ console.log(this.brand+","+this.color+" computer"); } }; var macbookpro1=new Computer(); var macbookpro2=new Computer();
【注】以上在字面变量中添加constructor属性,并将其指向Computer对象,是因为使用字面变量的方式整体重写了Computer.prototype对象,造成Computer.prototype.constructor不是指向默认的Computer对象。
使用原型对象会有意想不到的效果,比如动态地添加一些属性。但同时,由于原型中所有属性是被很多实例所共享的,如果有属性是引用型的,那么只要在一个实例中改变这个引用型属性的值,所有其它的实例也会被影响。
- 构造函数+原型模式
结合这两种模式,可以使得每个实例都会有一份自已的实例属性副本,同时也共享对方法的引用,最大限度节省了内存。如下:
function Computer(color,brand){ this.color=color; this.brand=brand; }; Computer.prototype={ constructor: Computer, description:function(){ console.log(this.brand+","+this.color+" computer"); } }; var macbookpro=new Computer("White","Apple"); var thinkpad=new Computer("black","IBM");
这种模式算是标准的模式了,如果觉得构造函数与原型分开定义了,可以使用动态原型,将原型放入构造方法中,如下:
function Computer(color,brand){ this.color=color; this.brand=brand; if(typeof this.description != 'function'){ Computer.prototype.description=function(){ console.log(this.brand+","+this.color+" computer"); }; } };