前言:

虽然ECMAScript从技术上讲是一门面向对象的语言,但是它不具备传统的面向对象语言所支持的类和接口等基本结构。但在ES5中,有与类相似的引用类型。引用类型描述一类对象所具有的属性和方法,引用类型的值(对象)是引用类型的一个实例,所以也被称为对象定义。

新对象是使用new 操作符后跟一个构造函数来创建的。ES5提供了多种原生引用类型(如Object, Array,Date, Function,RegExp等),也支持创建自定义的构造函数,从而定义自定义的对象类型。


一:自定义对象类型

如创建一个Person构造函数:

function Person(name,age) {
	this.name = name;
	this.age = age;
	this.sayName = function() {
		alert(this.name);
	}
}

下面用new操作符创建Person的新实例:

var person1 = new Person("Tom", 22);
var person2 = new Person("Jerry", 27);

这种方式调用构造函数会经历下面4个步骤:

(1)创建一个新对象;
(2)将构造函数的作用域赋给新对象(使this指向新对象);
(3)执行构造函数中的代码(为这个新对象添加属性);
(4)返回新对象。

person1和person2既是Object的实例,也是Person 的实例,且都有一个constructor(构造函数)属性指向Person。

二:构造函数与其他函数的对比

构造函数其实也是函数,任何函数,只要通过new操作符来调用,那它就可以作为构造函数;如果不用new调用,那它也就是个普通函数。所以实际上不存在所谓的“构造函数”,只有对于函数的“构造调用”。如上面的Person()函数可以有下面两种调用方法:

//当构造函数使用
var person = new Person("Tom",22);
person.sayName();//"Tom"

//作为普通函数使用
Person("Jerry",27);
window.sayName();//"Jerry"

三:构造函数的问题

使用构造函数来创建多个实例对象时,构造函数模式的缺点就会暴露出来。就是每一个方法都要在每个实例上重新创建一遍。也就是说,在前面的例子中,person1和person2各有一个sayName()方法,且这两个同名函数是不相等的。所以,构造函数模式创建了两个完成同样任务的函数,这是没有必要的,所以可以把代码改写成下面所示:

function Person(name,age) {
	this.name = name;
	this.age = age;
	this.sayName = sayName;
}

function sayName(){
	alert(this.name);
}
var person1 = new Person("Tom", 22);
var person2 = new Person("Jerry", 27);

在上面的代码中,我们把sayName()函数从构造函数内部移出来了,并在构造函数内部定义了其sayName属性为sayName()函数。这样,person1和person2 可以共享全局作用域中的同一个函数。但是这样又会有新问题产生:如果对象需要定义多个方法,那么就得定义多个全局函数,那样我们自定义的引用类型就没有丝毫封装性可言了。所以我们引入了原型模式来解决。下一篇我们来看原型模式。