今天我来说说什么是构造函数。
在说构造函数之前,我们首先需要要知道JavaScript创建对象的方式有两种
- 对象字面量方式
- 使用new表达式方式
- 对象字面量是一种灵活的书写方式,例如:
var a1={
P:"my name is cy",
alertp:function(){
alert(this.p);
}
}
这样,就用对象字面量的方式创建了一个对象a1,它具有成员变量p和成员方法alertp。这种写法不需要定义一个构造函数,因此不在本文的讨论范围之内。这种写法的缺点是,没创建一个新的对象都需要写出完整的定义 语句,不便于大量的创建相同的对象,不便于使用继承等高级特性。
- 使用new表达式方法创建对象。
例如:使用new String(“a string”),调用内置的String函数构造了一个字符串对象。下面我们用构造函数的方式来创建一个实现功能与上面一直的对象,首先定义构造函数,然后调用new表达式:
function a2(){
this.p="my name is cy";
this.alertp=function(){
alert(this.p)
}
}
var b1=new a2();
那么使用new操作符来电泳一个构造函数的时候,发生了什么呢?其实很简单,就发生了四件事情。
- var b1={};
- b1._proto=a2.prototype;
- a2.call(b1);
- return b1;
- 第一 创建一个空的b1
- 第二 将这个对象的proto成员指向构造函数对象a2的prototype成员对象。
- 第三 将构造函数的作用域赋值给新的对象,因此a2函数的this指向了新的对象b1,然后再调用a2函数。于是我们就给b2对象赋值了一个成员变量p ,这个成员变量的值是“my name is cy”
- 返回新的对象b1 。当构造函数里包含返回语句时情况比较特殊,这种情况会在下文中介绍。
正确定义构造函数
不同于其他的主流编程语言,JavaScript的构造函数并不是作为类的一个特定的方法存在,当任意一个普通函数用于创建一类对象是,它就被称为构造函数,或者是构造器。一个函数要作为一个真正的构造函数,需要满足一下条件。
- 在函数内部新对象(this)的属性进行设置,同城是添加属性和方法。
- 构造函数可以包含返回语句(不推荐),但返回值必须是this,或者其他非对象类型的值。
上文定义的构造函数a2就是一个标准的、简单的构造函数。下面例子定义函数a3返回了一个对象,我们可以使用new表达式来调用它,该表达式可以正确的返回一个对象。
function a3(){
var o={
p:"my name is cy";
}
return o;
}
var b2=new a3();
alert(a3.p)
这种方式不是值得推荐的方式,因为对象b2的原型是函数a3内部的对象o的原型,也就是Object.prototype。这种方式相当于执行科new表达式的前三步,而在第四步的时候返回了a3函数 的返回值。该方式同样不便于创建大量的相同对象,不利于使用继承等高级特性,并且容易造成混乱,应该摒弃。
一个构造函数在某种情况下完全可以作为普通的功能函数来使用,这是JavaScript灵活性的一个体现。下例定义的a4就是一个多用途的函数。
function C2(a, b){
this.p = a + b;
this.alertP = function(){
alert(this.p);
}
return this.p;//此返回语句在C2作为构造函数时没有意义
}
var c2 = new C2(2,3);
c2.alertP();//结果为5
alert(C2(2, 3)); //结果为5
该函数既可以用作构造函数来构造一个对象,也可以作为普通的函数来使用。用作普通函数时,它接收两个参数,并返回两者的相加的结果。为了代码的可读性和可维护性,建议作为构造函数的函数不要掺杂除构造作用以外的代码;同样的,一般的功能函数也不要用作构造对象。
为什么要使用构造函数
根据上文的定义,在表面上看来,构造函数似乎只是对一个新创建的对象进行初始化,增加一些成员变量和方法;然而构造函数的作用远不止这些。为了说明使用构造函数的意义,我们先来回顾一下前文提到的例子。执行var o2 = new CO();创建对象的时候,发生了四件事情:
- var b1={};
- b1._proto=a2.prototype;
- a2.call(b1);
- return b1;
我们说最重要的是第二步,将新生成的对象的prop属性赋值为构造函数的prototype属性,使得通过构造函数创建的所有对象可以共享相同的原型。这意味着同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的对象。关于原型(prototype)和继承的细节,笔者会再另一篇文章中深入说明。
在JavaScript标准中,并没有prop这个属性,不过它现在已经是一些主流的JavaScript执行环境默认的一个标准属性,用于指向构造函数的原型。该属性是默认不可见的,而且在各执行环境中实现的细节不尽相同,例如IE浏览器中不存在该属性。我们只要知道JavaScript对象内部存在指向构造函数原型的指针就可以了,这个指针是在调用new表达式的时候自动赋值的,并且我们不应该去修改它。
在构造对象的四个步骤中,我们可以看到,除第二步以外,别的步骤我们无须借助new表达式去实现,因此new表达式不仅仅是对这四个步骤的简化,也是要实现继承的必经之路。
网友评论