|
通常一个构造函数是这样子的,它带有一个area的原型方法,在构造函数中传入长、宽并计算面积。
var Shape = function(width, height) {
this.width = width;
this.height = height;
};
Shape.prototype.area = function() {
return this.width * this.height
};
var shape = new Shape(20, 30);
shape.area();
> 600
输出很完美,这也是经典JavaScript实现继承的方法,通过prototype添加对另外一个对象的引用。
不过一部分JavaScript程序是非常讨厌new关键字的,这个关键字有太浓烈的Java烙印了,而且掩盖了JavaScript基于原型的本质,降低了程序员使用JS语言的效率,因此你想把它省了,看看结果?
注* "JavaScript The Good Parts“的作者Douglas Crockford 曾写道 "A much better alternative is to not use new at all." (page 49), 参见另一程序员对AngularJS的吐嘈, 你已经毁了JavaScript(吐嘈之一就是里面到处都是new)
var shape = Shape(20, 30);
shape.area();
> TypeError: Cannot read property 'area' of undefined
道理再简单不过了,你只不过是执行了一个函数,但是这个函数并没有返回值,所以shape必然是undefined,看来new关键作用之一跟Java是一样的,就是实例化此对象并返回这个对象本身。所以你又试改写了一下函数:
var Shape = function(width, height) {
var self = this;
self.area = function() {
return width * height;
}
return self;
};
var shape = Shape(20, 30);
shape.area();
> 600
不错,It works!也不用实例化Shape了,看上去一切正常,代码看上去更简单了,隐藏了不必要的属性,不过似乎你没有意识到你设计了一个潜在的更大的坑,尝试在console中打印这些:
area
> function () { return self.width * self.height; }
为什么are变成了全局变量?你不死心,又在window里打印
window.area
> function () {
return width * height;
}
结果还一样,其实这一现象在"Javascript: The Good Parts"一书中有很好的解析:
If you forget to include the new prefix when calling a constructor function, then this will not be bound to the new object. Sadly, this will be bound to the global object, so instead of augmenting your new object, you will be clobbering global variables. That is really bad. There is no compile warning, and there is no runtime warning. (page 49)
如果你在一个构造函数中忘了加new,那么这个新对象就不会绑定到新的实例上,不幸的是,它会绑定到全局对象上,你就会影响全局变量。这是非常糟糕的。没有编译警告,并且没有运行时的警告。(第49页)
看来在function函数内使用this绑定公开属性是非常危险的,因为你不确定会不会有人忘写了一个new,或者故意不加一个new,也许这就是为什么基于function的构造函数现在用得越来越少的原因吧,其实解决的办法非常简单,只需更改一处。
var Shape = function(width, height) {
var self = {};
self.area = function() {
return width * height;
}
return self;
};
var shape = Shape(20, 30);
shape.area();
>600
其实还有很多其它的解决方案,还有一些开源项目甚至根本不使用构造函数,在此不再累述。 |
|