js中的构造函数可用来创建特定类型的对象,像Object和Array的原生构造函数,在运行时会自动出现在执行环境中,此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如:
在这里构造函数名应该以一个大写的字母开头,而非构造函数则应该以一个小写字母开头,这种做法,主要是为了区别于js中的其他函数,因为构造函数本身也是函数,只不过可以用来创建对象而已。
要创建一个Person的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历以下4个步骤,
1.创建一个新对象
2.将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象
在前面的例子,person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如下所示。
对象的constructor属性最初是用来标识对象类型的。但是,提到检测对象类型,还是instanceof操作符要更靠谱一些。我们在这个例子中创建的所有对象既是Object的实例,同时也是Person的实例,这一点通过instanceof操作符可以得到验证。
创建自定义的构造函数意味着将来可以将他的实例标识为一种特定的类型,而这正是构造函数模式胜过工厂模式的地方。在这个例子中,person1和person2之所以同时是Object的实例,是因为所有对象均继承子Object
将构造函数当作函数
构造函数与其他函数的唯一区别就是调用他们的方式不同。不过构造函数毕竟也是函数。任何函数只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那他跟普通函数没什么两样,例如前面的Person也可以这样调用:
构造函数的问题
构造函数模式虽然好用,但也并非没有缺点。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍,在前面的几个例子中,person1和person2都拥有一个名为sayName()的方法,但那两个方法不是同一个function的实例。不要忘了,js中的函数都是对象,因此每定义一个函数,也就实例化了一个对象,从逻辑角度讲,此时的构造函数也可以这样定义。
然而,创建两个完成同样任务的Function实例的确没有必要,况且有this对象在。根本不用在执行代码前就把函数绑定到特定对象上面,因此,大可像下面这样,通过把函数转移到构造函数外部来解决这个问题,
在这个例子中,我们把sayName()函数的定义转移到了构造函数外部,而在构造函数外部,我们将sayName属性设置成等于全局的sayName函数,这样一来,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数,这样做确实解决了两个函数做同一件事的问题,可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域只能被某个对象调用,这样全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义函数的引用类型就丝毫没有封装性可言了。好在我们可以通过使用原型模式来解决。