在功能上,以这种方式构建我的代码有什么缺点吗?将原型方法添加到构造函数体内的原型对象中(即在构造函数的表达式语句关闭之前)会导致意外的作用域问题吗?
是的,存在缺点和意外的范围界定问题。
一遍又一遍地将原型分配给本地定义的函数,每次都重复该分配并创建一个新的函数对象。较早的分配将被垃圾收集,因为它们不再被引用,但与第二个代码块相比,在构造函数的运行时执行和垃圾收集方面都是不必要的工作。
在某些情况下会出现意外的范围界定问题。有关Counter
明确示例,请参阅我的答案末尾的示例。如果您从原型方法中引用构造函数的局部变量,那么您的第一个示例会在您的代码中创建一个潜在的令人讨厌的错误。
还有一些其他(更小的)差异。您的第一个方案禁止在构造函数之外使用原型,如下所示:
Filter.prototype.checkProduct.apply(someFilterLikeObject, ...)
而且,当然,如果有人使用:
Object.create(Filter.prototype)
不运行Filter
构造函数,这也会产生不同的结果,这可能不太可能,因为期望使用Filter
原型的东西应该运行Filter
构造函数以达到预期的结果是合理的。
从运行时性能的角度来看(在对象上调用方法的性能),你最好这样做:
var Filter = function( category, value ){
this.category = category;
this.value = value;
// product is a JSON object
this.checkProduct = function( product ){
// run some checks
return is_match;
}
};
有一些 Javascript“专家”声称不再需要使用原型来节省内存(我几天前看过一个关于这个的视频讲座)所以是时候开始直接在对象上使用更好的方法性能而不是比原型。我不知道我是否准备好自己提倡这一点,但这是一个有趣的思考点。
我能想到的第一种方法的最大缺点是它真的非常容易犯下令人讨厌的编程错误。如果您碰巧认为您可以利用原型方法现在可以看到构造函数的局部变量这一事实,那么一旦您拥有多个对象的实例,您就会迅速地射中自己的脚。想象一下这种情况:
var Counter = function(initialValue){
var value = initialValue;
// product is a JSON object
Counter.prototype.get = function() {
return value++;
}
};
var c1 = new Counter(0);
var c2 = new Counter(10);
console.log(c1.get()); // outputs 10, should output 0
问题演示:http : //jsfiddle.net/jfriend00/c7natr3d/
这是因为,虽然看起来该get
方法形成了一个闭包并且可以访问作为构造函数的局部变量的实例变量,但它在实践中并不是这样工作的。因为所有实例共享同一个原型对象,对象的每个新实例Counter
都会创建一个新的get
函数实例(它可以访问刚刚创建的实例的构造函数局部变量)并将其分配给原型,所以现在所有实例都有一个get
访问最后创建的实例的构造函数的局部变量的方法。这是一场编程灾难,因为这可能永远不是预期的,并且很容易让人头疼,以找出问题所在以及原因。