Javascript
中的构造函数与其他语言相比也是不同的。任何通过关键字 new
调用的函数都可以当做构造函数。
在构造函数体内,this
指向新创建的对象。如果构造函数体内没有显示的 return
表达式,那么我们就默认返回 this
,也就是新建的对象。
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
上面的代码将 Foo
作为构造函数进行调用,并将新建对象的原型(__proto__)
指向了 Foo.prototype
。
如果我们在构造函数内定义返回的 return
表达式,构造函数就会返回整个表达式,但这个返回表达式必须为一个对象。
function Bar() {
return 2;
}
new Bar(); // a new object
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // the returned object
如果 new
被省略,那么函数将不能返回一个新的对象。
function Foo() {
this.bla = 1; // gets set on the global object
}
Foo(); // undefined
上面的例子可能在某些场景下也可以运行,但由于 Javascript
中 this
的工作机制,这里 this
将指向全局对象。
工厂模式
为了能够不使用关键字 new
,构造函数将不得不显示返回一个值。
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
上例中使不使用 new
来调用函数 Bar
达到的效果是一样的,将会返回一个新建的包含 method
方法的对象,这里实际上就是一个闭包。
这里需要注意一点,new Bar()
将不会返回 Bar.prototype
,而是在 return
表达式内函数 method
的原型对象。
上例中,使用 new
与否在功能上是无差异的。
通过工厂模式创建新的对象
我们经常被提醒不要使用 new
,因为一旦忘记了它的使用将导致错误。
为了创建一个对象,我们更愿意使用工厂模式并在工厂模式内构造一个新的对象。
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
尽管上例代码比使用 new
时更不容易出错,而且在使用私有变量时将更加方便,但同时也有一些不好的地方:
- 因为不能共享原型对象,所以需要更多的内存。
- 为了实现继承,工厂模式需要拷贝另一个对象的所有方法或者将其作为新对象的原型。
- 放弃原型链只是为了避免使用
new
,这似乎与Javascript
语言的精神相悖。
总结
尽管使用 new
可能比较容易产生错误,但这并不能成为放弃使用原型链的原因。至于最后采取哪种方式,这需要根据应用的需求而定。最好的方式就是选择一种风格并坚持下去。
参考
http://bonsaiden.github.io/JavaScript-Garden/#function.constructors
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。