【问题标题】:Private and privileged methods vs prototype methods私有和特权方法与原型方法
【发布时间】:2011-01-30 01:18:05
【问题描述】:

在 JavaScript 中,我可以通过在构造函数中声明它们来创建私有和特权方法。通过这个,我必须将它们移出对象的原型。然后我失去了继承的可能性和一些性能,因为每个对象都将拥有这些方法的自己的副本,而不是访问一个原型对象。

所以我现在的问题是什么可能是一个 petter 模式:是否使用私有和特权方法。我不喜欢晃来晃去,所以我想避免这种情况。那么该怎么办呢?

你有什么经验?

【问题讨论】:

    标签: javascript oop prototypal-inheritance


    【解决方案1】:

    我从未见过在 JavaScript 中创建所谓的“私有”函数有什么价值。只需以某种方式标记它们以表明它们不是您的公共 API 的一部分,因此 API 客户端不能保证该函数将存在,或者在未来的版本中具有相同的实现。

    除了 API 一致性之外,没有理由不让人们只使用您的私有函数(如果他们愿意)。当然,它允许共存脚本与您的私有函数混淆,但无论如何,这些脚本可能已经覆盖了您的公共 API 函数。

    这个问题的公认答案对此有很好的评论: Private functions in namespaced javascript

    【讨论】:

    • 我要补充一点,命名空间是一个好主意,而不是任何打算在 JS 方面共享的东西。此外,在 JS 中尝试使用模拟 OO 风格的继承是一种可怕的模式。 SOLID 可能更好,但没有什么是完美的。
    • 对于在一小群人之间共享的顶级实现,私有成员可能可以忽略不计,但是像具有大量分布式用户群的库之类的东西都将从私有成员中受益:尤其是在使用带有代码完成。
    【解决方案2】:

    约定是限定需要的范围...并让伪私有或受保护成员在您的方法或属性前加上下划线。

    我更喜欢伪私有/受保护范围...

    var MyObject = (function(){
      var interalStaticVar;
    
      function ctor(arg1, arg2) {
        //create psuedo-protected context
        this._ = {};
    
        //create a psuedo-private context
        this.__ = {};
    
        //stash someval
        this.__.someVal = "hands off";
      }
    
      ctor.prototype.getSomethingPrivate = function() {
        return this.__.someVal;
      }
    
      ctor.prototype._doSomethingProtected = function(){ ... }
    
      ctor.prototype.__doSomethingPrivate = function() { ... }
    
      return ctor;
    }());

    我会说尝试将 OO 风格的继承范例应用于 JavaScript 是在自找麻烦,并且可能意味着您做错了什么。我倾向于遵循更多的 SOLID 设计,在浏览器中包含 JS 的功能性事件驱动特性。

    【讨论】:

    • 你可以变得更优雅,并使用 this['\0xff'] 或其他可能嗅觉不好的东西。
    【解决方案3】:

    您可以使用模拟私有或内部方法和属性

    obj._foo = private;
    
    obj.prototype._internalMethod;
    

    您需要将私有方法与继承分开。任何可以在不依赖继承的情况下使用的东西都可以正常工作。还有这样的模式:

    function construct() { 
        var priv;
    
        this.someValue = foo;
        this.someMethod = function() { }
    }
    

    这里我们忽略原型,直接写入对象。这种依赖闭包来隐藏方法和变量的模式适用于 mixins。

    担心在构造函数中重新声明方法是否效率低下是微优化和。如果您不希望创建至少 1000 个对象,则差异可以忽略不计。

    【讨论】:

      【解决方案4】:

      我只是猜测你有一些函数在构造函数中使用,但不是公共 API 的一部分。

      在这种情况下,一种选择是简单地将它们作为属性存储在构造函数中。然后在构造函数中调用它们时,您将使用.call() 方法来设置当前正在构造的对象的上下文。

      这样您就可以通过this 自动访问公共成员,如果您将它们作为参数传递,也可以访问私有变量。

      var myClass = function() {
          this.someprop = 'prop value';
          var privVar = 'private value';
      
          myClass.func.call(this, privVar);
      };
      
      myClass.func = function(priv){ 
          alert(this.someprop);
          alert(priv); 
      };
      
      var inst = new myClass;
      

      所以这只是使用MyClass 函数作为构造函数使用的函数的命名空间存储。构造函数从this 的上下文中调用它,它对this.someProperty 和传入的privVar 执行一些操作。

      不确定这是否是您所追求的,但这是一种选择。


      正如@Raynos 在下面的 cmets 中正确指出的那样,作为简单命名空间存储添加到 MyClass 构造函数的任何属性根本不是私有的,并且可以被任何可以访问构造函数的代码访问。

      您可以进行一些检查以帮助确保正确调用它,例如添加 instanceof 检查以确保它是从 MyClass 的实例调用的,但这样的检查根本不安全并且不提供保护实际的私人成员。

      【讨论】:

      • @Patrickdw 除了以牺牲可读性为代价的微优化之外,它与私有函数有何不同?
      • @Raynos:你一针见血。这可能是微优化,当然无助于可读性。这只是一种可用于实现 OP (我认为)要求的模式。它不一定好或坏,但我猜它是否值得使用将取决于OP。
      • @patrickdw 在这种情况下,请始终保持可读性。您总是可以在以后手动优化真正的瓶颈。
      • @Raynos:我明白你在说什么,我同意在大多数情况下,最好只使用私有函数。虽然有时避免额外的开销可能是有意义的,在这种情况下,这样的事情可能是值得的。您的 cmets 和答案对此解决方案提供了很好的反击视角。
      • @Patrickdw 你的权利,这是一个有效的替代方案,但确实提到你可以在继承的对象中调用该方法,而这是你无法用私有函数做的。 IE。在SubClass中调用Super.func
      【解决方案5】:

      我认为这完全取决于您想如何使用您的对象。如果您需要某个对象中的私有变量/函数,并且希望它们可以被该对象的原型方法访问,您必须在构造函数中声明原型方法(顺便看看example here)。

      在我看来,它归结为程序的设计和使用。如果您需要具有真正私有变量或 - 函数的对象,请按照我给出的示例进行设计,否则不要。根据我的经验,私有/特权函数并没有真正影响性能。

      【讨论】:

        【解决方案6】:

        在创建 javascript 对象时,我通常遵循以下模式。

        var builders = (function() {
        var privateMethod = null;
        function builders() {
                    this.a_priviledged_method = function() {};
                    this.pointerToPrototypeMethod = __bind(this.prototypeMethod, this);
            }
            builders.prototype.prototypeMethod = function(){};
            return builders;
        })();
        // general method to bind a function call to a context
        var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
        

        注意事项:

        1. 'privateMethod' 不是私有的,因为它不在构造函数内部,但通过确定它的范围,可以将其对将要创建的每个构建器实例设为私有。
        2. 确保在“创建它们”的上下文中调用原型方法,而不是在“调用它们的上下文”中调用。我创建了一个伪特权方法,该方法在创建时使用上下文的 curry 原型方法。对原型方法的后续调用将始终在创建时的上下文中发生。
        3. 由于 javascript 的方法查找机制规定应最后检查原型链,因此“pointerToPrototypeMethod”将在“prototypeMethod”之前“发现”。
        4. 本可以实现类似于“a_priviledged_method”的“pointerToPrototypeMethod”,但在这种情况下,每个“构建器”实例都将拥有自己的特权方法副本,从而导致代码复制。
        5. 在调用私有方法时,应确保通过提供上下文来调用它。例如:在 'a_priviledged_method' 的主体中,如果想要调用 'privateMethod',则不应将其称为 privateMethod(),而应将其称为 privateMethod.apply(this,args)。优点是,如果想要从私有方法调用特权方法,那么它将在相同的上下文中(与调用私有方法的内容相同)。

        希望这会有所帮助。

        【讨论】:

          【解决方案7】:
          (function () {
              function privateFunc () { ... }
          
              A.prototype.publicFunc1 = function () { ... };
              A.prototype.publicFunc2 = function () { ... };
          
          })();
          
          function A () { ... }
          

          我编辑:如果您还需要私有变量,这将无济于事。

          【讨论】:

          • -1 令人困惑的示例代码。私有函数无权访问thisA 中声明的私有变量
          • 如果它可以访问另一个函数中的私有变量,它们就不是私有的,不是吗?我觉得这个评论令人困惑。私有函数不是方法,但您可以根据需要传递对象。
          • 一般模式是构造函数中的私有变量和方法。不同闭包中的私有方法无权访问A 对象中的任何数据。
          • 正如我所说,它不是一个方法,它是一个函数。它需要的任何数据都通过公共方法传递给它。
          • 离开本次讨论的主题,但不要忘记将立即调用的函数设为表达式。现在它是一个声明,因此不能立即调用。
          猜你喜欢
          • 2016-03-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-11
          • 2015-03-19
          • 1970-01-01
          • 1970-01-01
          • 2011-08-09
          • 2012-07-14
          相关资源
          最近更新 更多