在构造函数内部分配原型方法* - 为什么不呢?

Jam*_*ton 5 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;
  }

};
Run Code Online (Sandbox Code Playgroud)

对于这种结构:

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;
}
Run Code Online (Sandbox Code Playgroud)

从功能上讲,以这种方式构造我的代码有什么缺点吗?将原型方法添加到构造函数体内的原型对象(即在构造函数的表达式语句关闭之前)会导致意外的范围问题吗?

我之前已经成功使用了第一个结构,但是我想确保自己没有为调试带来麻烦,或者由于编码错误导致开发人员悲痛和恶化.

jfr*_*d00 11

从功能上讲,以这种方式构造我的代码有什么缺点吗?将原型方法添加到构造函数体内的原型对象(即在构造函数的表达式语句关闭之前)会导致意外的范围问题吗?

是的,有缺点和意外的范围问题.

  1. 将原型一遍又一遍地分配给本地定义的函数,两者都重复该赋值并每次创建一个新的函数对象.早期的分配将被垃圾收集,因为它们不再被引用,但是与第二个代码块相比,它在构造函数的运行时执行和垃圾收集方面都是不必要的工作.

  2. 在某些情况下存在意外的范围问题.有关Counter明确示例,请参阅我的答案末尾的示例.如果从prototype方法引用构造函数的局部变量,那么您的第一个示例会在代码中创建一个可能令人讨厌的错误.

还有一些其他(更小的)差异.您的第一个方案禁止在构造函数之外使用原型,如:

Filter.prototype.checkProduct.apply(someFilterLikeObject, ...)
Run Code Online (Sandbox Code Playgroud)

当然,如果有人使用:

Object.create(Filter.prototype) 
Run Code Online (Sandbox Code Playgroud)

在不运行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;
  }

};
Run Code Online (Sandbox Code Playgroud)

有一些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
Run Code Online (Sandbox Code Playgroud)

演示问题:http://jsfiddle.net/jfriend00/c7natr3d/

这是因为,虽然看起来该get方法形成一个闭包并且可以访问作为构造函数的局部变量的实例变量,但它在实践中并不起作用.因为所有实例共享相同的原型对象,所以Counter对象的每个新实例都会创建一个新的get函数实例(可以访问刚创建的实例的构造函数局部变量)并将其分配给原型,所以现在所有实例都有一个get访问创建的最后一个实例的构造函数的局部变量的方法.这是一个编程灾难,因为这可能永远不会是预期的,并且可能很容易成为弄清楚出了什么问题的原因.