JavaScript代码体系结构-是否使用构造函数

JavaScript代码体系结构-是否使用构造函数,javascript,unobtrusive-javascript,Javascript,Unobtrusive Javascript,请帮助我决定是使用函数的原型对象和“new”关键字,还是完全远离构造函数 情况: 函数名为widget(),它将被调用10-15次以初始化页面上的每个小部件widget()包含许多内部方法 每次调用widget()时,函数都需要返回一个对象,该对象充当对小部件进行操作的API 问题 1) 我是否将Widget()中的所有内部方法置于其prototype属性下?这没有意义,但主要原因是不要每次调用widget()时都重新实例化内部函数 function widget() { var retu

请帮助我决定是使用函数的原型对象和“new”关键字,还是完全远离构造函数

情况:

函数名为
widget()
,它将被调用10-15次以初始化页面上的每个小部件
widget()
包含许多内部方法

每次调用
widget()
时,函数都需要返回一个对象,该对象充当对小部件进行操作的API

问题

1) 我是否将
Widget()中的所有内部方法置于其prototype属性下?这没有意义,但主要原因是不要每次调用
widget()
时都重新实例化内部函数

function widget()
{
   var returnObj = {};

   /* Add internal functions but this will be re-instantiated every time */

   return returnObj;

}
但是如果我把内部函数放在原型中,每个实例化的
w
对象(
w=newwidget();
)都可以访问内部私有方法

2) 如果我远离构造函数和
new
关键字,并按照下面的方式构造代码,那么如何解决每次调用
widget()
时重新实例化的内部函数的性能问题

function widget()
{
   var returnObj = {};

   /* Add internal functions but this will be re-instantiated every time */

   return returnObj;

}

你在这里有点折衷。您似乎已经了解,您在
.prototype
上放置的方法是公开的,但这是放置方法的最有效的地方,因为它们会以非常有效的方式自动添加到该对象的所有新副本中。对方法使用
.prototype
时,方法只有一个副本,对该副本的引用将自动添加到该对象的所有新实例化中

但是,javascript没有内置私有方法,唯一的解决方法是不使用
.prototype
来调用私有方法

这很好地描述了如何为任何对象中的数据或方法创建隐私

无论是哪种情况,我都看不出有任何理由避免使用
new
关键字来创建新对象。您可以使
.prototype
或私有方法与
new
一起工作

但是,如果您想要实现真正的私有方法,那么您不能对私有方法或需要访问它们的任何方法使用
.prototype
,因此您必须决定哪个对您更重要。没有单一的正确答案,因为您对隐私的需求是特定于具体情况的

在我的编码中,我通常不强制执行隐私,而是使用
.prototype
new
。我在原型上指定“非公共”方法,方法的名称以下划线开头。这是一种符号约定,而不是访问强制方案

关于避免使用
新的
操作符和重新实例化方法的第二个问题,我想问你为什么要这样做?你得到了什么?我不知道使用
new
有什么坏处。据我所知,您关于是否在构造函数中使用
.prototype
与手动创建/分配方法的决定应该与对私有方法的需求有关


仅供参考,无论哪种方式,15个对象都不会在性能上产生显著差异。我会评估你对真正隐私的需求,并据此做出决定。如果您必须强制执行隐私,那么使用Crockford方法来实现私有方法。如果你不需要真正的隐私,那么就使用
.prototype
。在这两种情况下,我都没有理由避免使用
new

您可以使用metaconstructor*模式来解决这个问题

function defineCtor(metaCtor) {

  var proto = new metaCtor();

  var ctor = proto.hasOwnProperty('constructor') ? 
             proto.constructor : new Function();

  return (ctor.prototype = proto).constructor = ctor;

}
现在您有了一个构造构造函数(或者更准确地说,构造原型并返回构造函数)的函数


解释

defineCtor
将单个匿名函数作为属性。它使用
new
调用函数,创建一个对象。它将对象指定为新构造函数的原型属性(空函数或生成的原型对象自己的
constructor
属性),并返回该函数

这为内部函数提供了一个闭包,解决了问题1,并为您设置了构造函数/原型对,解决了问题2


比较

defineCtor
技术与以下两个示例进行比较

这个例子使用了原型,问题1:内部的东西没有封装

function Widget(options) {
  this.options = options;
}

Widget.prototype = {

  getFoo: function() {
    return doInternalStuff();
  }

};

// How to encapsulate this?
function doInternalStuff() { /* ... */ }
此示例在构造函数中设置所有内容,但存在问题2:每次构造对象时,都会为每个属性实例化新的函数对象

function Widget(options) {

  this.options = options;

  function doInternalStuff() { /* ... */ }

  this.getFoo = function() {
    return doInternalStuff();
  };

}
此示例使用上述技术提供封装,同时仍利用原型:

var Widget = defineCtor(function() { 
  //                    ^
  // This function runs once, constructing the prototype.

  // In here, `this` refers to the prototype.

  // The real constructor.
  this.constructor = function(options) {

    // In function properties, `this` is an object instance 
    // with the outer `this` in its prototype chain.

    this.options = options;

  };  

  function doInternalStuff() { /* ... */ }

  this.getFoo = function() { return doInternalStuff(); };

});

// ...

var myWidget = new Widget();

这种方法有一些好处,有些比其他方法更明显

  • 它提供了封装。您可以通过将第一个“比较”示例包装到一个立即调用的函数中来实现这一点,但是这种方法可能更干净,并且在团队设置中更容易“实施”

  • 它是可扩展的。您可以为“元构造函数”函数提供它们自己的原型,函数属性如“extends”、“mixin”等。然后,在
    metaCtor
    的主体中,您可以编写类似于
    this.extends(BaseWidget)
    的内容。
    defineCtor
    API永远不需要更改,任何情况都会发生

  • 它“欺骗”Google闭包编译器、Eclipse、jsdoc等,使其认为您是在定义实际的构造函数,而不是“元函数”。这在某些情况下是有用的(代码是以这些工具理解的方式“自文档化的”)

*据我所知,“元构造函数”这个词完全是疯了