簡體   English   中英

構造函數中的原型關閉

[英]prototype closure in the constructor

更新:

這種實現方式很糟糕,我已經刪除了該答案。


我只是回答了這個問題 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並清理了對對象的所有A.prototype.myFunc引用A.prototype.myFunc ,也存在疑問,當gc出現時它可能仍然存在。
  4. 我的測試環境有限,很高興知道此實現是否存在潛在風險。

所以我認為這個問題的答案可能是:

答:一種更可行的方法來更改構造函數中的原型方法,以實現只能在原型方法中訪問私有成員。

B.實現相同目的的另一種方法,並且代碼盡可能地簡單。

指出我對您的答案中的閉包和垃圾回收的誤解也將不勝感激。

讓我們在另一個問題中查看OP的要求:

是否存在模仿“受保護”對象屬性的JavaScript模式

答:最好的方式(以我的觀點)將它們命名為_myPrivate

順便說一句-我不希望特權成員函數的模式訪問私有屬性,因為成員函數仍然是公共的。

這根本沒有任何意義,OP認為A.prototype.myFunc無法在A實例上公開訪問嗎?

可以在此處找到原型和構造函數的簡介(以及一些私有模式)

我傾向於人們說同意“只是不帶私人打擾”,但我認為要做到這一點的最好辦法,如果你真的想要的話,是有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;
}

此函數使您可以定義具有“公共”和“私有”范圍的偽類。 這個想法是:

  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

1。 它需要一個額外的功能來確保不對此進行引用。

沒有解決方法。 that是通過捕獲A.prototype.myFunc每個實例中,與實例本身是可以訪問的對象that直接,更對象涉及只會讓事情變得更糟; retire方法已經是取消引用的最簡單方法。

2。 之后,隨着對象的創建,A.prototype.myFunc越來越大。

這只是潛在的風險。 A.prototype.myFunc與遞歸方法類似,但實際上並非如此 它調用前一個myFunc並檢查實例的身份。 在少數情況下這不是問題,但在許多情況下,深度的增加最終會導致堆棧溢出

由於實現將需要任何清理機制,因此使調用更深層次的內容不會僅使用數組來保存引用並按需清理而獲得任何好處。

3。 由於每個var myFunc仍由A.prototype.myFunc引用,因此即使在調用retire並清理了對對象的所有外部引用之后,也存在疑問,當gc出現時它可能仍然存在。

事實是A.prototype.myFunc捕獲的var myFunc仍然存在,即使gc來收集垃圾。 幾乎不可能釋放對myFunc的引用,因為它是鏈式調用,更深層調用和淺層調用的上下文彼此不可見,因此它們都不能夠修改調用鏈用於跳過水平 ; 取消設置myFunc只會破壞鏈條。 任何試圖解決此問題的技巧都將涉及更多對象,這可能會增加成本,也可能會顯得過大。

4。 我的測試環境有限,很高興知道此實現是否存在潛在風險。

作為要點2的答案,當使用它創建許多對象時,它可能導致堆棧溢出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM