自定义构造函数
除了对象字面量和内置构造函数之外,你也可以通过自定义的构造函数来创建对象实例,正如下面的代码所示:
var adam = new Person("Adam");
adam.say(); // "I am Adam"
这种写法非常像Java中用Person
类创建了一个实例,两者的语法非常接近,但实际上JavaScript中没有类的概念,Person()
是一个函数。
Person()
构造函数是如何定义的呢?看下面的代码:
var Person = function (name) {
this.name = name;
this.say = function () {
return "I am " + this.name;
};
};
当你通过new
来调用这个构造函数时,函数体内将发生这些事情:
- 创建一个空对象,将它的引用赋给
this
,并继承函数的原型。 - 通过
this
将属性和方法添加至这个对象。 - 最后返回this指向的新对象(如果没有手动返回其他的对象)。
用代码表示这个过程如下:
var Person = function (name) {
// 使用对象字面量创建新对象
// var this = {};
// 添加属性和方法
this.name = name;
this.say = function () {
return "I am " + this.name;
};
//return this;
};
上例中,为简便起见,say()
方法被添加至this
中,结果就是不论何时调用new Person()
,在内存中都会创建一个新函数(say()
),显然这是效率很低的,因为所有实例的say()
方法是一模一样的。最好的办法是将方法添加至Person()
的原型中。
Person.prototype.say = function () {
return "I am " + this.name;
};
我们将会在下一章里详细讨论原型和继承,现在只要记住将需要重用的成员放在原型里即可。
关于构造函数的内部工作机制也会在后续章节中有更细致的讨论。这里我们只做概要的介绍。刚才提到,构造函数执行的时候,首先创建一个新对象,并将它的引用赋给this
:
// var this = {};
其实事实并不完全是这样,因为“空”对象并不是真的空,这个对象继承了Person
的原型,看起来更像:
// var this = Object.create(Person.prototype);
在后续章节会进一步讨论Object.create()
。
构造函数的返回值
当使用new
调用的时候,构造函数总是会返回一个对象,默认情况下返回this
所指向的对象。如果构造函数内没有给this
赋任何属性,则返回一个“空”对象(除了继承构造函数的原型之外,没有自有属性)。
尽管在构造函数中没有return
语句的情况下,也会隐式返回this
。但事实上我们是可以返回任意指定的对象的,在下面的例子中就返回了新创建的that
对象。
var Objectmaker = function () {
// name属性会被忽略,因为返回的是另一个对象
this.name = "This is it";
// 创建并返回一个新对象
var that = {};
that.name = "And that's that";
return that;
};
// 测试
var o = new Objectmaker();
console.log(o.name); // "And that's that"
可以看到,构造函数中其实是可以返回任意对象的,只要你返回的东西是对象即可。如果返回值不是对象(字符串、数字或布尔值),程序不会报错,但这个返回值被忽略,最终还是返回this
所指的对象。