JavaScript模块模式 - 受保护的成员?

时间:2012-01-02 17:44:06

标签: javascript inheritance

喂!这是我的第一个问题!

我正在尝试Doug Crockford和其他人推广的模块模式。到目前为止,我对它非常满意,但我对处理某种继承模式的最佳方法有些不确定。

我把它归结为使用猫和哺乳动物的裸骨盒,尽管我的目的是为帆布制作基于瓷砖的游戏的对象。

但是这是我使用浏览器警报的裸骨'动物'案例:

var ZOO = ZOO || {};
//
ZOO.mammal = function () {
   "use strict";
   var voice = "squeak.mp3", // default mammal sound
      utter = function () {
         window.alert(this.voice);
      };
//
   // public interface
   return {
      utter: utter,
      voice: voice
   };
};
//
ZOO.cat = function () {
   "use strict";
   // hook up ancestor
   var thisCat = ZOO.mammal();
   thisCat.voice = "miaw.mp3";
   return thisCat;
};
//
var felix = ZOO.cat();
felix.utter();

让我对这种方法感到困扰的是,我不得不将voice作为公共财产,以便猫可以修改它。

我真正想要的是“受保护”的可见性(来自Java,ActionScript等),以便cat可以修改voice,而没有任何人可以访问felix修改它。

有解决方案吗?

3 个答案:

答案 0 :(得分:14)

您可以通过将空白对象传递到基础“类”来充当受保护属性的存储库,从而模拟受保护的可见性(对您自己和子对象可见)。这将允许您通过继承链共享属性,而不会将其公开。

var ZOO = ZOO || {};

ZOO.mammal = function (protectedInfo) {
   "use strict";
   protectedInfo = protectedInfo || {};
   protectedInfo.voice = "squeak.mp3";

   // public interface
   return {
      utter: function () {
         alert(protectedInfo.voice);
      }
   };
};

ZOO.cat = function () {
   "use strict";

   var protectedInfo = {};
   // hook up ancestor
   var thisCat = ZOO.mammal(protectedInfo);

   protectedInfo.voice = "miaw.mp3";
   return thisCat;
};

这是live demo

答案 1 :(得分:1)

Sidesteping non-answer:

有一些方法可以在Javascript中获得受保护的属性,但它们不一定非常惯用。如果我是你,我会首先强烈考虑

  1. 使用以下划线开头的公共属性约定(例如:_voice)来表示隐私。它非常简单,是动态语言中的标准。

  2. 寻求没有继承的替代解决方案。继承经常使事情复杂化并将事物耦合在一起,因此旧的“更喜欢构成而非继承”的口头禅。 Javascript有很多功能,比如duck typing和高阶函数,它们通常可以避免在Java中正常需要它的情况下使用继承

答案 2 :(得分:1)

有一种解决方法可以模拟受保护的成员,您可以将这些成员公开一段时间,然后再次对其进行私有化。我不是这方面的忠实粉丝,但它是一个"解决方案"。

我刚引用此SitePoint article

  

添加受保护的会员

     

将脚本拆分为多个模块是一种常见且方便的方法   实践。它使大型代码库更易于管理和允许   当不总是需要模块时,可以节省带宽。

     

但是如果我们想在不同模块之间共享数据呢?要是我们   将这些数据公开,然后我们将失去隐私的好处,但如果   我们将其设为私有,它只能用于一个模块。我们是什么   真正需要的是共享的私人成员,这些被称为   保护。

     

JavaScript没有受保护的成员,但我们可以   通过暂时公开数据来有效地创建它们。实现   首先,我先向您介绍两个关键功能 - 扩展和   私有化 - 我们将其定义为实用函数对象的一部分:

var utils = {
  extend : function(root, props) {
    for(var key in props) {
      if(props.hasOwnProperty(key)) {
        root[key] = props[key];
      }
    } return root;
  },
  privatise : function(root, prop) {
    var data = root[prop];
    try { delete root[prop]; } catch(ex) { root[prop] = null; }
    return data;
  }
};
     

extend函数只是向对象添加新属性,而   privatize函数复制属性,然后删除原始属性。我们   可以在一个模块中使用extend来创建对私有的公共引用   变量,然后在另一个模块中使用私有化将其复制回   私有变量并删除公共引用。

     

所以这是第一个有两个受保护模块的例子   成员(包括utils对象本身)和一个公共成员。至   保持代码示例简短,实用程序功能只是空的   贝壳,但它们与我向你展示的功能相同   片刻前:

var MyModule = (function() {
  var myProtectedData = 909;
  var utils = {
    extend : function(root, props) { },
    privatise : function(root, prop) { }
  };
  this.myPublicData = 42;
  return utils.extend(this, { myProtectedData : myProtectedData, utils : utils });
})();
     

你可以看到我们如何使用揭示模块模式的变体,   不仅要返回公共成员,还要将受保护成员返回   好。所以在这一点上我们有三个公共成员:   MyModule.myProtectedData,MyModule.utils和MyModule.myPublicData。

     

现在这是使用私有化的最后一个模块的示例   用于将指定的公共成员复制回私有的功能   变量,然后删除它们的公共引用:

var MyModule = (function() {
  var myProtectedData = this.utils.privatise(this, 'myProtectedData');
  var utils = this.utils.privatise(this, 'utils');
  return this;
}).apply(MyModule);
     

一旦完成,受保护的成员就被锁定在他们的内部   对象,两个模块都可以私下使用,但不再可用   可以从他们外面获得。

     

请注意,privatize函数依赖于具有单独的参数   对于object和property-key,因为JavaScript中的对象是   通过引用传递。所以root是对MyModule的引用,当我们的时候   从键中指定的属性中删除属性,我们将其删除   引用对象的属性。

     

但如果是这样的话:

privatise : function(root) {
  var data = root;
  try { delete root; } catch(ex) { root = null; } return data;
}
     

并且这样称呼:

var myProtectedData = this.utils.privatise(this.myProtectedData);
     

然后公共成员不会被删除 - 功能会   只需删除引用,而不是它引用的属性。

     

对于旧的IE版本,try ... catch构造也是必需的,   其中不支持删除。在这种情况下,我们使公众无效   属性而不是删除它,这显然是不一样的,但是   具有否定会员公众的等效最终结果   参考