构造函数中的原型闭包

时间:2013-11-22 15:58:48

标签: javascript closures

更新:

这种实现很糟糕,我已经删除了这个答案。


我刚回答this question。 OP要求私有成员的解决方案,只能通过原型方法访问。对于我的回答,我不建议这样做,而是提出其可能性的代码。 (对不起,我对标题没有好主意......)

  • 代码

    function A(prop1) {
        var myFunc=A.prototype.myFunc;
        var that=this;
    
        A.prototype.myFunc=function () {
            if (this===that) {
                alert(prop1); // do something
            }
            else {
                myFunc.call(this);
            }
        };
    
        this.retire=function () {
            that=undefined;
        };
    }
    
    A.prototype.myFunc=function () {
    };
    
    var a1=new A(1);
    var a2=new A(2);
    var a3=new A(3);
    
    a1.myFunc();
    a2.myFunc();
    a3.myFunc();
    
    a2.retire();
    
    a1.myFunc();
    a2.myFunc();
    a3.myFunc();
    
    // .. 
    

我们可以看到是否有其他原型方法可以访问prop1,需要重复这种模式。我曾经想过使用私有数组来实现它,但这段代码似乎要短得多。

但有些事情并不好:

  1. 需要额外的功能以确保that不引用this
  2. 随着对象的创建,
  3. A.prototype.myFunc正在成长(更深)。
  4. 由于var myFunc仍然引用了每个A.prototype.myFunc,因此即使在调用retire并清除对象的所有外部引用之后也存在疑问,当gc出现时它可能仍然存在。
  5. 我的测试环境有限,很高兴知道这种实现是否存在潜在风险。
  6. 所以我认为这个问题的答案可能是:

    一个。一种更可行的方法来改变构造函数中的原型方法,以实现私有成员只能在原型方法中访问。

    B中。另一种实现同样的方法,并且代码尽可能简单。

    我还要非常感激地指出我对你的答案中的闭包和垃圾收集的误解。

3 个答案:

答案 0 :(得分:1)

让我们在另一个问题中看到OP的要求:

  

是否存在模仿“受保护”对象的JavaScript模式   特性

答案:某种,最好的方式(在我看来)将它们命名为_myPrivate

  

BTW - 我不想要特权成员函数的模式   因为成员函数仍然存在,所以访问私有属性   公共

根本没有任何意义,OP是否认为A.prototype.myFunc在A个实例上无法公开访问?

可以找到原型和构造函数的介绍(以及私有的一些模式)here

答案 1 :(得分:1)

我倾向于同意那些说“只是不打扰私人”的人,但我认为,如果你真的想要它,最好的方法就是{{1} }。 Crockford的文章没有提到这种方法,可能是因为它早于Function#bind,并且用bind模仿bind会有点毛茸茸(或者可能是因为它的额外开销不大增益)。

apply

此功能允许您定义具有“公共”和“私有”范围的伪类。这个想法是:

  1. 公共范围对象放置在私有范围对象的原型链中。
  2. 所有函数都绑定到私有作用域对象。
  3. 首次尝试

    function classify(fn) {
      var privateScope = {}, publicScope = {};
    
      function bindProp(to, target, src, key) {
        if (!src.hasOwnProperty(key)) return;
        if (!(src[key] && src[key].bind)) return;
        target[key] = src[key].bind(to);
      }
      function ctor() {
          var instancePublic = {}, instancePrivate = Object.create(instancePublic);
    
        for (var key in publicScope) {
          bindProp(instancePrivate, instancePublic, publicScope, key);
        }
        for (var key in privateScope) {
          instancePrivate[key] = privateScope[key];
        }
        if (publicScope.hasOwnProperty('constructor'))
          publicScope.constructor.apply(instancePrivate, arguments);
    
        return instancePublic;
      }
      fn.call(publicScope, publicScope, privateScope);
    
      return ctor;
    }
    

    此版本的原型链反转:

    1. 私有范围对象放置在公共范围对象的原型链中。
    2. 所有函数都绑定到私有作用域对象。
    3. 任何未被公众成员遮蔽的私人会员都被function classify(fn) { var privateScope = {}, publicScope = {}; function bindProp(privateScope, scopeObject, key) { if (!scopeObject.hasOwnProperty(key)) return true; if (!(scopeObject[key] && scopeObject[key].bind)) return; privateScope[key] = scopeObject[key].bind(privateScope); } function ctor() { var instancePrivate = Object.create(privateScope), instancePublic = Object.create(instancePrivate); for (var key in publicScope) { console.log(key); bindProp(instancePrivate, publicScope, key); } for (var key in privateScope) { if (!bindProp(instancePrivate, privateScope, key) && !publicScope.hasOwnProperty(key)) instancePublic[key] = void 0; } if (publicScope.hasOwnProperty('constructor')) publicScope.constructor.apply(instancePrivate, arguments); return instancePublic; } fn(publicScope, privateScope); return ctor; } 遮蔽。
    4. <强>用法

      您可以使用以下内容:

      undefined

      你可以在控制台中玩这个,看看它的效果就像你可能期望的那样。

      var Foo = classify(function(pub, priv) {
      
        // constructors are supported but not required
        pub.constructor = function(a, b) {
          this.a = a;
          this.b = b;
        };
      
        priv.somePrivateProp = "lol";
      
        priv.doPrivateStuff = function(x, y) {
          return x + y;
        };
      
        pub.somePublicProp = "rofl";
      
        pub.doStuff = function(x, y) {
          return this.doPrivateStuff(x + 1, y + 1) + ' ' + this.somePrivateProp;
        };
      
      });
      

答案 2 :(得分:1)

  

1。它需要一个额外的功能,以确保不参考这一点。

没有解决方法。 that在每个实例化中捕获A.prototype.myFunc,实例本身就是可以直接访问that的对象,涉及的对象越多,就会使事情变得更糟; retire方法已经是解开引用的最简单方法。

  

2。 A.prototype.myFunc随着对象的创建而成长(更深)。

这只是潜在的风险。 A.prototype.myFunc与递归方法类似,但实际上不是。它调用前一个myFunc并检查实例的身份。对于少数情况,这不是问题,但对于大量实例,增长的深度最终将导致堆栈溢出

由于实现将需要一个清理机制,为了使调用更深入,只需使用数组来保存引用,并按需清理。

  

3。因为每个var myFunc仍然被A.prototype.myFunc引用,所以即使在调用了退出并清理对象的所有outter引用之后也存在疑问,当gc到来时它可能仍然存在。

即使gc来收集垃圾,var myFunc捕获的A.prototype.myFunc仍然存活。几乎不可能可以释放对myFunc的引用,因为它是链式调用,更深层调用的上下文和浅层调用没有相互之间的可见性,因此它们都不能修改跳过级别的调用链; unset myFunc只会破坏链条。试图解决这个问题的任何技巧都会涉及更多的对象,这可能会增加成本或者成为一种过度杀伤力。

  

4。我的测试环境有限,很高兴知道这种实现是否存在潜在的风险。

作为第2点的答案,当用它创建大量对象时,它可能导致堆栈溢出。