构造函数中的原型闭包

prototype closure in the constructor

本文关键字:闭包 原型 构造函数      更新时间:2023-09-26

更新:

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


我刚刚回答了这个问题。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. A.prototype.myFunc是随着后来的对象创建而成长(更深层次)的
  3. 由于每个var myFunc仍然被A.prototype.myFunc引用,因此即使在调用retire并清除对对象的所有外部引用之后,它也可能在gc到来时仍然有效,这是有疑问的
  4. 我的测试环境有限,很高兴知道这个实现是否存在潜在风险

所以我认为这个问题的答案可能是:

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

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

如果能在你的回答中指出我对关闭和垃圾收集的误解,我将不胜感激。

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

是否存在模仿"受保护"对象的JavaScript模式属性

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

BTW-我不想要特权成员函数的模式访问私有属性,因为成员函数仍然平民的

这根本没有意义,OP是否认为A.prototype.myFunc在A实例上是不可公开访问的?

原型和构造函数的介绍(加上一些私有模式)可以在这里找到

1。它需要一个额外的功能来确保不引用这个

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

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

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

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

3。由于每个var-myFunc仍然被A.prototype.myFunc引用,因此即使在调用了retired并清除了对对象的所有outter引用之后,它也可能在gc到来时仍然有效,这是有疑问的

事实上,即使gc来收集垃圾,A.prototype.myFunc捕获的var myFunc仍然有效。几乎有不可能发布对myFunc的引用,因为它是一个链式调用,更深调用和浅调用的上下文彼此不可见,因此它们都无法修改跳过一个级别的调用链;未设置的CCD_ 19只会破坏链。任何试图解决这个问题的技巧都会涉及更多的对象,这可能会增加成本,也可能是过度使用。

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

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

我倾向于同意人们说"不要打扰私人"的说法,但我认为,如果你真的想要,最好的方法是使用Function#bind。Crockford的文章没有提到这种方法,可能是因为它早于bind,用apply模拟bind有点麻烦(也可能是因为这是一个额外的开销,不会带来太多好处)。

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

这个函数允许您定义一个具有"public"answers"private"作用域的伪类。想法是:

  1. 公共作用域对象被放置在私有作用域对象的原型链中
  2. 所有函数都绑定到专用作用域对象

第一次尝试

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

这个版本的原型链颠倒了:

  1. 私有作用域对象被放置在公共作用域对象的原型链中
  2. 所有函数都绑定到专用作用域对象
  3. 任何未被公共成员遮蔽的私有成员都被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;
  };
});

你可以在控制台上玩这个,看看它是否像你预期的那样工作。

var foo = new Foo('abc', 123);
foo.doStuff(3, 5); // "10 lol"
foo.doPrivateStuff(3, 5) // throws TypeError