JavaScript模块模式保护成员?

JavaScript模块模式保护成员?,javascript,inheritance,Javascript,Inheritance,喂!这是我的第一个问题 我正在试验Doug Crockford和其他人提出的模块模式。到目前为止,我对它非常满意,但我有点不确定处理某种继承模式的最佳方式 我把它归结为一个使用猫和哺乳动物的裸体游戏,尽管我的实际意图是在画布上为基于瓷砖的游戏制作对象 但以下是我使用浏览器警报的裸体“动物”案例: var ZOO = ZOO || {}; // ZOO.mammal = function () { "use strict"; var voice = "squeak.mp3", // d

喂!这是我的第一个问题

我正在试验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();
这种方法让我烦恼的是,我不得不将
语音
作为公共属性,以便cat可以修改它

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


有解决方案吗?

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

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;
};

这里有一个回避不回答的问题:

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

  • 使用带有下划线(例如:
    \u voice
    )的公共财产惯例表示隐私。它非常简单,是动态语言中的一个标准

  • 寻找没有继承的替代解决方案。继承往往会使事情复杂化,使事情复杂化,因此有一句古老的“宁可创作也不要继承”的咒语。Javascript有很多特性,比如duck类型和高阶函数,这些特性通常可以让您避免在Java中通常需要继承的情况下使用继承


  • 有一种变通方法可以模拟受保护的成员,将这些成员公开一段时间,然后再将其私有化。我不太喜欢这个,但这是一个“解决方案”

    我只是引用这句话:

    添加受保护的成员

    将脚本拆分为多个模块是一种常见且方便的方法 练习。它使大型代码库更易于管理,并允许 在不总是需要模块的情况下节省带宽

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

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

    这是第一个模块的示例,它有两个受保护的 成员(包括utils对象本身)和一个公共成员。到 保持代码示例简短,实用程序函数只是空的 shell,但它们与我在 刚才:

    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);
    
    完成后,受保护的成员将被锁定在其内部 对象,这两个模块都可以私下使用,但不再使用 可从外部获取

    请注意,私有化函数依赖于具有单独的参数 对于对象和属性键,因为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);
    
    并这样称呼:

    privatise : function(root) {
      var data = root;
      try { delete root; } catch(ex) { root = null; } return data;
    }
    
    var myProtectedData = this.utils.privatise(this.myProtectedData);
    
    然后公众成员将不会被删除-功能将被删除 只需删除引用,而不是它引用的属性

    试一试。。。catch构造对于较旧的IE版本也是必要的, 其中不支持删除。在这种情况下,我们取消了公众的权利 属性而不是删除它,这显然是不一样的,但是 其最终结果是否定该成员的公开声明 参考资料


    我认为Douglas Crockford不同意你的观点:
    到目前为止,我们看到的继承模式的一个弱点是我们没有隐私。对象的所有属性都是可见的。我们没有私有变量,也没有私有方法。有时这并不重要,但有时这很重要。令人沮丧的是,一些不知情的程序员采用了假装隐私的模式。如果他们有一处想要私有化的房产,他们会给它起一个奇怪的名字。。。。幸运的是,在模块模式的应用程序中,我们有一个更好的选择。
    [Crockford 52]假装隐私不仅仅是“出于挫折而生”——它非常简单和严格,并且与原型继承JS native配合得很好