ECMA-262把对象定义为:”无需属性的集合,其属性可以包含基本值、对象或者函数。”严格来讲,这就相当于说明对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为这样,我们可以把ECMAScript的对象想象成散列表:无非就是一组名对值,其中值可以是数据或函数。
创建自定义对象最简单的方式就是创建一个Object的实例,然后再为他添加属性和方法,如下所示:
var person = new Object();、person.name = "liubei";person.age = 29; person.job = "shayemuyou"; person.sayName = function(){ alert(this.name); }
上面的例子创建了一个名为person的对象,并为他添加了三个属性和一个方法。其中sayName()方法用于显示name属性,this.name将被解析为person.name,早期的开发人员经常使用这个模式来创建对象,后来对象字面量的方法成了创建对象的首选模式,上面的例子用对象字面量的语法可以写成如下这样:
var person = { name:"liubei", age:29, job:"shayemuyou", sayName:function(){ alert(this.name); } }
这个例子中的person对象和前面的对象是一样的,都有相同的属性和方法。
虽然Object构造函数或者对象字面量的方法都可以用来创建单个对象,但是这些方法有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个方法,人们开始使用工厂模式的一种变体。
一、工厂模式
工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。考虑到ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下所示:
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } return o; } var person1 = createPerson("wei",25,"software"); var person2 = createPerson("bu",25,"software");
函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以多次调用这个函数,每次都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题,即怎么样知道这是哪个对象类型。
二、构造函数模式
像Array、Object这样的原生构造函数,在运行时会自动出现在执行环境中。此外,我们可以创建自定义个构造函数,从而定义自定义类型的属性和方法。例如,我们可以使用构造函数重写上个例子:
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new Person("wei",25,"software"); var person2 = new Person("bu",25,"software");
在这个例子中,Person()函数取代了createPerson()函数,我们注意到Person()与createPerson()的不同之处在于:
没有显式的创建对象
直接将属性和方法赋值给this对象
没有return语句
此外,还应该注意到函数名Person使用的是大写字母P。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。这个做法借鉴了其他OO语言,主要是为了区别于ECMAScript中的其他函数。因为构造函数本身也是函数,只不过可以创建对象而已。
要创建一个Person实例,必须使用new操作符。以上这种方式会经过以下四个步骤:
1、创建一个新对象
2、将构造函数的作用域赋给新对象(因此this指向这个新对象)
3、执行构造函数中的代码
4、返回新对象
在前面例子的最后,person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person。如下:
console.log(person1.constructor == Person); //true console.log(person2.constructor == Person); //true
对象的constructor属性最初是用来标识对象类型的。但是,提到检测对象类型,还是instanceof操作符比较可靠一些。我们在这个例子中创建的对象都是Object对象的实例,也是Person对象的实例,这一点通过instanceof操作符可以验证。
console.log(person1 instanceof Object); //trueconsole.log(person1 instanceof Person); //trueconsole.log(person2 instanceof Object); //trueconsole.log(person2 instanceof Person); //true
创建自定义的构造函数意味着将来可以将他的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。在这个例子中,person1和person2之所以同是Object的实例,是因为所有的对象都继承自Object。