Javascript 在构造函数函数中指定原型方法-为什么不?
在风格上,我更喜欢这种结构:Javascript 在构造函数函数中指定原型方法-为什么不?,javascript,methods,scope,prototype,Javascript,Methods,Scope,Prototype,在风格上,我更喜欢这种结构: 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
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;
}
对于该结构:
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;
}
从功能上讲,这样构造代码有什么缺点吗?将原型方法添加到构造函数主体内的原型对象(即在构造函数的表达式语句关闭之前)是否会导致意外的作用域问题
我以前成功地使用过第一种结构,但我想确保我没有因为糟糕的编码实践而让自己陷入调试头疼的境地,或者让其他开发人员感到悲伤和烦恼。第一种示例代码有点没有达到原型的目的。您将为每个实例重新创建checkProduct方法。虽然它将仅在原型上定义,并且不会为每个实例消耗内存,但它仍然需要时间 如果希望封装该类,可以在声明checkProduct方法之前检查该方法是否存在:
if(!Filter.prototype.checkProduct) {
Filter.prototype.checkProduct = function( product ){
// run some checks
return is_match;
}
}
还有一件事你应该考虑。该匿名函数的闭包现在可以访问构造函数中的所有变量,因此访问它们可能很有诱惑力,但这会让您陷入一个兔子洞,因为该函数只对单个实例的闭包有权限。在你的例子中,它将是最后一个例子,在我的例子中,它将是第一个
从功能上讲,这样构造代码有什么缺点吗?
将向中的原型对象添加原型方法
构造函数的主体(即在构造函数的
表达式语句关闭)是否导致意外的作用域问题
是的,存在缺陷和意外的范围界定问题
计数器
示例,了解明确的示例。如果从prototype方法中引用构造函数的局部变量,那么第一个示例会在代码中创建一个潜在的严重bugFilter.prototype.checkProduct.apply(someFilterLikeObject, ...)
当然,如果有人使用:
Object.create(Filter.prototype)
如果不运行过滤器
构造函数,这也会产生不同的结果,这可能不太可能,因为有理由期望使用过滤器
原型的东西应该运行过滤器
构造函数,以获得预期的结果
从运行时性能的角度(在对象上调用方法的性能)来看,您最好这样做:
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“专家”声称不再需要使用原型节省内存(几天前我观看了一个关于这一点的视频讲座),因此是时候开始直接在对象上而不是原型上使用性能更好的方法了。我不知道我自己是否已经准备好宣传这一点,但这是一个值得思考的有趣的观点
我能想到的第一种方法的最大缺点是,它非常非常容易犯严重的编程错误。如果您碰巧认为可以利用prototype方法现在可以看到构造函数的局部变量这一事实,那么当您有多个对象实例时,您就会很快地自食其果。想象一下这种情况:
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
问题的演示:
这是因为,虽然它看起来像是
get
方法形成了一个闭包,并且可以访问作为构造函数的局部变量的实例变量,但实际上它不是这样工作的。由于所有实例共享同一原型对象,因此计数器
对象的每个新实例都会创建get
函数的一个新实例(该函数可以访问刚刚创建的实例的构造函数局部变量),并将其分配给原型,因此,现在所有实例都有一个get
方法,该方法访问最后创建的实例的构造函数的局部变量。这是一场编程灾难,因为这可能从来都不是我们想要的,很容易让人摸不着头脑,弄不清哪里出了问题以及原因。在第一个示例中,Filter
原型在至少调用一次Filter
之前都没有填充函数。如果有人试图继承过滤器
原型,该怎么办?使用任一节点
function ExtendedFilter() {};
util.inherit(ExtendedFilter, Filter);
或对象。创建:
function ExtendedFilter() {};
ExtendedFilter.prototype = Object.create(Filter.prototype);
如果忘记或不知道首先调用过滤器
,则原型链中的原型始终为空。您的代码最大的缺点是可能会覆盖您的方法
如果我写:
Filter.prototype.checkProduct = function( product ){
// run some checks
return different_result;
}
var a = new Filter(p1,p2);
a.checkProduct(product);
结果将不同于预期,因为将调用原始函数,而不是my。当其他答案集中在从构造函数内部分配给原型的错误之处时,我将集中在您的第一句话:
在风格上,我更喜欢这种结构
您可能喜欢这种表示法提供的简洁性—属于类的所有内容都被{}
块正确地“限定”到它的范围。(当然,谬误在于它的作用域是构造函数的每次运行)
我建议您看看JavaScript提供的(揭示性的)功能。您可以获得更显式的结构、独立的构造函数声明、类范围的私有变量,以及正确封装在块中的所有内容:
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;
}());
仅供参考,您也不能安全地执行此操作