在构造函数里面分配原型方法*为什么不呢?
在风格上,我更喜欢这种结构:
var Filter = function( category, value ){ this.category = category; this.value = value; // product is a JSON object Filter.prototype.checkProduct = function( product ){ // run some checks return is_match; } };
对于这个结构:
var Filter = function( category, value ){ this.category = category; this.value = value; };// var Filter = function(){...} Filter.prototype.checkProduct = function( product ){ // run some checks return is_match; }
在function上,这样构build我的代码有没有什么缺点? 将原型方法添加到构造函数体内的原型对象(即在构造函数的expression式语句closures之前)会导致意外的范围问题?
我以前使用过第一个结构,但是我想确保我不会让自己陷入debugging困境,或者由于不好的编码习惯而导致开发人员的悲痛和激动。
在function上,这样构build我的代码有没有什么缺点? 将原型方法添加到构造函数体内的原型对象(即在构造函数的expression式语句closures之前)会导致意外的范围问题?
是的,有缺陷和意想不到的范围界定问题。
-
将原型反复分配给本地定义的函数,都重复该分配并每次创build一个新的函数对象。 之前的任务将被垃圾收集,因为它们不再被引用,但是与第二个代码块相比,在构造函数的运行时执行和垃圾收集方面都是不必要的。
-
在某些情况下有意想不到的范围界定问题。 在我的答案结尾处查看
Counter
示例。 如果您从原型方法引用构造函数的局部variables,那么您的第一个示例会在您的代码中创build一个潜在的恶意错误。
还有一些(更小的)差异。 您的第一个scheme禁止在构造函数之外使用原型,如下所示:
Filter.prototype.checkProduct.apply(someFilterLikeObject, ...)
当然,如果有人使用:
Object.create(Filter.prototype)
而不运行Filter
构造函数,这也会创build一个不同的结果,这可能不太可能,因为期望使用Filter
原型的某些东西应该运行Filter
构造函数以实现预期的结果是合理的。
从运行时性能的angular度来看(在对象上调用方法的性能),你最好用这个:
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“专家”声称,不再需要使用原型的内存节省(我前几天观看了一个video讲座),所以现在是时候开始直接在对象上使用更好的方法性能了比原型。 我不知道我是否准备好自己提倡这一点,但这是一个值得思考的问题。
我能想到的第一种方法最大的缺点是,编写一个令人讨厌的编程错误真的很容易。 如果您碰巧认为您可以利用原型方法现在可以看到构造函数的局部variables这一事实,只要您有多个对象实例,就会立即将自己拍摄下来。 想象一下这种情况:
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
方法形成一个闭包,并且可以访问构造函数的局部variables的实例variables,但在实践中却不这样。 由于所有实例共享相同的原型对象,所以Counter
对象的每个新实例都会创build一个get
函数的新实例(可以访问刚刚创build的实例的构造函数局部variables)并将其分配给原型,所以现在所有实例有一个get
方法来访问创build的最后一个实例的构造函数的局部variables。 这是一个编程的灾难,因为这可能从来没有打算,可能很容易成为一个头痛的人找出什么地方出了问题,为什么。
第一个示例代码types错过了原型的目的。 您将重新创build每个实例的checkProduct方法。 虽然它只会在原型上定义,并且不会消耗每个实例的内存,但仍然需要时间。
如果你想封装类,你可以在声明checkProduct方法之前检查方法的存在:
if(!Filter.prototype.checkProduct) { Filter.prototype.checkProduct = function( product ){ // run some checks return is_match; } }
还有一件事你应该考虑。 这个匿名函数的闭包现在可以访问构造函数中的所有variables,所以它可能是诱人的访问它们,但是这会导致你陷入一个兔子洞,因为这个函数只会closures一个实例的闭包。 在你的例子中,这将是最后一个例子,在我的例子中,它将是第一个。
虽然其他答案都集中在从构造函数中分配给原型的问题上,但我会把重点放在第一个语句上:
在风格上,我更喜欢这种结构
大概你喜欢这个符号提供的干净的封装 – 属于这个类的所有东西都被{}
块正确地“限制”了。 (当然,这个谬误是它的构造函数每次运行的范围 )。
我build议你参考JavaScript提供的(揭示) 模块模式 。 你得到一个更加明确的结构,独立的构造函数声明,类作用域的私有variables,以及正确地封装在一个块中的所有东西:
var Filter = (function() { function Filter(category, value) { // the constructor this.category = category; this.value = value; } // product is a JSON object Filter.prototype.checkProduct = function(product) { // run some checks return is_match; }; return Filter; }());
你的代码最大的缺点是closures覆盖你的方法的可能性。
如果我写:
Filter.prototype.checkProduct = function( product ){ // run some checks return different_result; } var a = new Filter(p1,p2); a.checkProduct(product);
结果将会不同于预期的原函数将被调用,而不是我的。
在第一个例子中, Filter
原型没有被函数填充,直到Filter
被调用至less一次。 如果有人试图inheritance原型Filter
? 使用nodejs'
function ExtendedFilter() {}; util.inherit(ExtendedFilter, Filter);
或Object.create
:
function ExtendedFilter() {}; ExtendedFilter.prototype = Object.create(Filter.prototype);
如果忘记或者不知道先调用Filter
总是会以原型链中的空白原型结束。
只是FYI,你不能安全地做到这一点:
function Constr(){ const privateVar = 'this var is private'; this.__proto__.getPrivateVar = function(){ return privateVar; }; }
原因是因为Constr.prototype === this.__proto__
,所以你会有同样的不良行为。