2013-11-22 52 views
0

更新:原型封閉的構造

這種實現的僅僅是壞的,我已刪除了答案。


我剛回答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. A.prototype.myFunc隨着對象的創建而成長(更深)。
  3. 由於每個var myFunc仍被A.prototype.myFunc引用,所以即使在調用retire並清除對對象的所有異常引用之後仍有疑問,但它在gc出現時可能仍然存在。
  4. 我的測試環境有限,很高興知道此實施是否存在潛在風險。

所以我覺得一個問題的答案可能是:

A.一個比較可行的辦法,以改變在構造函數原型的方法來實現的私有成員只能在原型方法訪問。

B.另一種實現同樣目標的方法,代碼儘可能簡單。

我也非常感謝你指出我對你的答案中關閉和垃圾回收的誤解。

+2

您將實例之間共享方法的整個想法割裂開來。只是做'this.myFunc = function(){alert(prop1); };構造函數內部。它沒有利用原型,但你的方法不是。 – Tibos

+0

@Tibos:你說得對。代碼只是爲了這個要求的可能性。 –

回答

1

1。它需要一個額外的功能來確保不參考這個。

沒有解決方法。在每個實例化中,thatA.prototype.myFunc捕獲,並且實例本身是可以直接訪問that的對象,涉及的更多對象會使事情變得更糟; retire方法已經是解開參考的最簡單方法。

2。 A.prototype.myFunc隨着對象的創建而成長(更深)。

這只是潛在的風險。 A.prototype.myFunc類似於遞歸方法,但實際上其不是。它調用前面的myFunc並檢查實例的身份。對於少數情況,這不是問題,但對於大量的實例,日益增長的深度最終會導致堆棧溢出。因爲實現將需要任何需要清理的機制,爲了使調用更深入,沒有什麼比僅僅使用數組來保存引用並清理按需。

3。由於每個var myFunc仍然由A.prototype.myFunc引用,所以即使在調用退出並清除對對象的所有外部引用之後,仍然存在疑問,但在gc出現時它仍可能還活着。

事實是var myFunc這是由A.prototype.myFunc會還活着,甚至當GC來收集garbages抓獲。幾乎有不可能使myFunc的引用被釋放,因爲它是一個鏈接調用,深層調用的上下文和淺層調用不具有彼此的可見性,因此它們都不能修改跳過的的調用鏈;未設置myFunc只會打破鏈條。任何試圖解決這個問題的技巧都會涉及更多的對象,這可能會增加成本或者是過度殺傷。

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

作爲第2點的答案,當使用它創建大量對象時,可能會導致堆棧溢出。

1

讓我們來看看OP中的其他問題的要求:

是否有一個JavaScript的圖案,其模仿「保護」對象 性能

答:那種,最好的辦法(在我認爲)他們的名字_myPrivate

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

根本沒有意義,OP是否認爲A.prototype.myFunc在A實例上不公開訪問?

介紹原型和構造函數(加上一些圖案的士兵),可以發現here

+0

好點。未暴露給公衆但被繼承的成員*受到保護。但是,是的,原型方法必須是公開的,我錯過了OP試圖模擬受保護成員的觀點。 –

+0

@KenKin沒問題,我的回答沒有解決你的問題,但解決了原始問題的有效性。 – HMR

1

我傾向於人們說同意「只是不帶私人打擾」,但我認爲最好的方法要做到這一點,如果你真的想要它,是用Function#bind。克羅克福德的文章沒有提到這種方法,可能是因爲它早於bind,而模擬bindapply得到了一種毛茸茸的(或者可能是因爲它沒有多少收益是額外的開銷)。

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 
+0

嗯..我需要時間再讀一遍。 –

+0

@KenKin,是的,如果你有時間玩這個,我很好奇你的想法。有一些注意事項 - 一方面,繼承將是一種痛苦,而在實例化之後,公共屬性只應由成員函數設置,如果您希望私有函數遵守其新值 - 但它應該主要工作。忽略我以前的(現在刪除的)關於privates靜態的評論,現在已經修復了。 –

+0

我正在迴應禮儀,因爲你對我的想法感到好奇。但是,想到的事情仍然很複雜,我最終會想到繼承的可見性。 –