前言:
虽然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 可以共享全局作用域中的同一个函数。但是这样又会有新问题产生:如果对象需要定义多个方法,那么就得定义多个全局函数,那样我们自定义的引用类型就没有丝毫封装性可言了。所以我们引入了原型模式来解决。下一篇我们来看原型模式。