2009-01-27 63 views
17

嗯,我試圖找出這是可能的任何方式。這裏是代碼:原型上的私人成員

a=function(text) 
{ 
    var b=text; 
    if (!arguments.callee.prototype.get) 
     arguments.callee.prototype.get=function() 
    { 
     return b; 
    } 
    else 
     alert('already created!'); 
} 

var c=new a("test"); // creates prototype instance of getter 
var d=new a("ojoj"); // alerts already created 
alert(c.get())  // alerts test 
alert(d.get())  // alerts test from context of creating prototype function :(

正如你看到我試圖創建原型getter。爲了什麼?那麼,如果你寫的是這樣的:

a=function(text) 
{ 
    var b=text; 
    this.getText=function(){ return b} 
} 

...一切都應該罰款..但其實我每次創建對象 - 我創建一個使用內存的getText功能。我想有一個原型功能躺在記憶裏,會做同樣的...任何想法?

編輯:

我試圖通過克里斯托夫給出的解決方案,它似乎是其唯一已知的,現在的解決方案。它需要記住ID信息以從上下文中檢索值,但是對我來說整個想法都很好:) ID只是需要記住的一件事,其他所有內容都可以在內存中存儲一​​次。事實上,您可以通過這種方式存儲大量私人成員,並隨時使用唯一的ID。其實這令我滿意:)(除非有人有更好的主意)。

someFunc = function() 
{ 
    var store = new Array(); 
    var guid=0; 
    var someFunc = function(text) 
    { 
    this.__guid=guid; 
    store[guid++]=text; 
    } 

    someFunc.prototype.getValue=function() 
    { 
    return store[this.__guid]; 
    } 

    return someFunc; 
}() 

a=new someFunc("test"); 
b=new someFunc("test2"); 

alert(a.getValue()); 
alert(b.getValue()); 
+0

這似乎是[另一個問題]的副本(http://stackoverflow.com/questions/436120) – 2009-01-27 13:07:49

回答

28

JavaScript傳統上並未提供財產隱藏機制('私人成員')。

由於JavaScript是詞法範圍的,您可以在每個對象級別上通過使用構造函數作爲「私有成員」的閉包並在構造函數中定義方法來模擬此操作,但這不適用於在構造函數的prototype屬性中定義的方法。

當然,有辦法解決這個問題,但我不會推薦:

Foo = (function() { 
    var store = {}, guid = 0; 

    function Foo() { 
     this.__guid = ++guid; 
     store[guid] = { bar : 'baz' }; 
    } 

    Foo.prototype.getBar = function() { 
     var privates = store[this.__guid]; 
     return privates.bar; 
    }; 

    Foo.prototype.destroy = function() { 
     delete store[this.__guid]; 
    }; 

    return Foo; 
})(); 

這將在另一個對象單獨的「私人」性質存儲從您的Foo實例。在完成對象之後,請確保致電destroy():否則,您剛剛創建了內存泄漏。


編輯2015年12月1日: ECMAScript6使得不需要手動物件破壞可能改進的變體,例如通過使用一個或WeakMap優選Symbol,避免了對外部存儲完全的需要:

var Foo = (function() { 
    var bar = Symbol('bar'); 

    function Foo() { 
     this[bar] = 'baz'; 
    } 

    Foo.prototype.getBar = function() { 
     return this[bar]; 
    }; 

    return Foo; 
})(); 
+0

這並不意味着我每次創建新的函數對象時都會調用新的Foo()? – Wilq32 2009-01-27 13:51:27

+0

有些東西在這裏看起來不正確......當Foo類有2個實例時,這個處理過程如何處理? – 2009-01-27 14:15:48

2

原型上的方法無法訪問「private」成員,因爲它們存在於javascript中;你需要某種特權存取器。既然你聲明get它在詞彙上可以看到b,它會一直返回創建時的b

2

在受到克里斯托夫的解決方案的巨大啓發後,我想出了一個略有修改的概念,提供了一些改進。再一次,這個解決方案很有趣,但不一定推薦。這些增強功能包括:

  • 不再需要在構造函數中執行任何設置
  • 刪除了需要公衆GUID存儲在這裏的情況
  • 增加了一些語法糖

本質的伎倆就是使用實例對象本身作爲訪問關聯私有對象的關鍵。通常這對於普通對象來說是不可能的,因爲它們的鍵必須是字符串。但是,我能夠使用表達式({} === {})返回false這一事實來實現此目的。換句話說,比較運算符可以在唯一對象實例之間辨別出

長話短說,我們可以用兩個數組維護實例和其相關的專用對象:

Foo = (function() { 
    var instances = [], privates = []; 

    // private object accessor function 
    function _(instance) { 
     var index = instances.indexOf(instance), privateObj; 

     if(index == -1) { 
      // Lazily associate instance with a new private object 
      instances.push(instance); 
      privates.push(privateObj = {}); 
     } 
     else { 
      // A privateObject has already been created, so grab that 
      privateObj = privates[index]; 
     } 
     return privateObj; 
    } 

    function Foo() { 
     _(this).bar = "This is a private bar!"; 
    } 

    Foo.prototype.getBar = function() { 
     return _(this).bar; 
    }; 

    return Foo; 
})(); 

你會發現上面的_功能。這是獲取私有對象的訪問器函數。它工作的很懶,所以如果你用一個新的實例調用它,它將立即創建一個新的私有對象。

如果你不想重複每個類別的_代碼,你可以通過它包裝工廠函數內部解決這個問題:

function createPrivateStore() { 
    var instances = [], privates = []; 

    return function (instance) { 
     // Same implementation as example above ... 
    }; 
} 

現在你可以減少每個班級它只是一條線:

var _ = createPrivateStore(); 

同樣,你必須非常小心使用此解決方案,因爲它可以創建內存泄漏,如果你不執行,並調用破壞functi在必要時進行。

-1

我創建了一個新的庫,用於在原型鏈上啓用私有方法。 https://github.com/TremayneChrist/ProtectJS

例子:

var MyObject = (function() { 

    // Create the object 
    function MyObject() {} 

    // Add methods to the prototype 
    MyObject.prototype = { 

    // This is our public method 
    public: function() { 
     console.log('PUBLIC method has been called'); 
    }, 

    // This is our private method, using (_) 
    _private: function() { 
     console.log('PRIVATE method has been called'); 
    } 
    } 

    return protect(MyObject); 

})(); 

// Create an instance of the object 
var mo = new MyObject(); 

// Call its methods 
mo.public(); // Pass 
mo._private(); // Fail 
0

就個人而言,我真的不喜歡用GUID的解決方案,因爲它迫使開發商宣佈它除了商店,以增加它在構造函數中。在大型JavaScript應用程序開發人員可能會忘記這樣做,這是很容易出錯。

我很喜歡Peter的回答,因爲您可以使用上下文(this)訪問私有成員。但是有一件事讓我非常困擾,就是這樣一個事實,即對私人成員的訪問完成得非常複雜。事實上,找到數組中對象的索引是一種線性算法。考慮你想使用這種模式來實現10000次的對象。然後,每次要訪問私有成員時,您都可以遍歷10000個實例。

爲了以一(1)個複雜度訪問私人商店,除了使用guid之外沒有別的辦法。但爲了不與GUID聲明和增量,爲了使用上下文來訪問專用存儲打擾我修改彼得斯工廠模式如下:

createPrivateStore = function() { 
var privates = {}, guid = 0; 

return function (instance) { 
    if (instance.__ajxguid__ === undefined) { 
     // Lazily associate instance with a new private object 
     var private_obj = {}; 
     instance.__ajxguid__ = ++guid; 
     privates[instance.__ajxguid__] = private_obj; 
     return private_obj; 
    } 

    return privates[instance.__ajxguid__]; 
} 

}

這裏的技巧是要考慮還沒有處理不具有屬性的對象。事實上,人們可以在第一次訪問商店之前手動設置屬性,但我認爲沒有不可思議的解決方案。

2

隨着現代瀏覽器採用一些ES6技術,您可以使用WeakMap來解決GUID問題。這部作品在IE11及以上:

// Scope private vars inside an IIFE 
var Foo = (function() { 
    // Store all the Foos, and garbage-collect them automatically 
    var fooMap = new WeakMap(); 

    var Foo = function(txt) { 
     var privateMethod = function() { 
      console.log(txt); 
     }; 
     // Store this Foo in the WeakMap 
     fooMap.set(this, {privateMethod: privateMethod}); 
    } 

    Foo.prototype = Object.create(Object.prototype); 
    Foo.prototype.public = function() { 
     fooMap.get(this).p(); 
    } 
    return Foo; 
}()); 

var foo1 = new Foo("This is foo1's private method"); 
var foo2 = new Foo("This is foo2's private method"); 
foo1.public(); // "This is foo1's private method" 
foo2.public(); // "This is foo2's private method" 

WeakMap它將被刪除或超出範圍,而且因爲它使用對象作爲鍵,你不需要附加的GUID後不會存儲任何Foo引用你的對象。