![](/img/trans.png)
[英]How does the JavaScript singleton pattern with cached static property work?
[英]How/why does this JS singleton pattern work?
我遇到了這種相當有趣的方式來創建一個可以用new
關鍵字實例化的JavaScript單例,比如var x = new SingletonClass()
。 我對變量范圍和閉包等有很好的把握,但是我很難理解為什么這段代碼按照它的方式工作。
// EDIT: DO NOT USE this code; see the answers below
function SingletonClass() {
this.instance = null;
var things = [];
function getInstance() {
if (!this.instance) {
this.instance = {
add: function(thing) {
things.push(thing);
},
list: function() {
console.log(things.toString());
}
};
}
return this.instance;
}
return getInstance();
}
var obj1 = new SingletonClass();
obj1.add("apple");
obj1.list(); //"apple"
var obj2 = new SingletonClass();
obj2.add("banana");
obj1.list(); //"apple,banana"
obj2.list(); //"apple,banana"
obj1.add("carrot");
obj1.list(); //"apple,banana,carrot"
obj2.list(); //"apple,banana,carrot"
我的直覺說,每一個新的時間SingletonClass
被實例化, this
指的是全新的對象-但后來因為一個完全獨立的對象由構造回來后,我會想出this
只會被丟棄。 但它仍然存在。 怎么樣? 為什么?
這里有一些微小的細節,我想念。 任何人都能發光嗎?
編輯:原來這個代碼很糟糕。 它“神奇地”似乎持有對實例的引用的原因是因為它實際上是靜默地將它存儲在全局對象中。 這充其量是不好的做法,毫無疑問容易出錯。
由不要混淆this
里面的功能getInstance
,那this
是全局對象window
,所以你創建了一個對象,並分配到窗口對象,下次調用構造函數的時候,你正在檢查是否window.instance
是現有。
代碼this.instance = null;
是沒有意義的,只是讓你感到困惑。 刪除它不會改變任何東西。
以下內容來自MDN 。
執行代碼
new foo(...)
,會發生以下情況:
- 創建一個新對象,繼承自foo.prototype。
- 使用指定的參數調用構造函數foo,並將其綁定到新創建的對象。 new foo相當於
new foo()
,即如果沒有指定參數列表,則不帶參數調用foo。- 構造函數返回的對象將成為整個新表達式的結果。 如果構造函數未顯式返回對象,則使用在步驟1中創建的對象。 (通常構造函數不返回值,但如果他們想要覆蓋正常的對象創建過程,他們可以選擇這樣做。)
注意step3,當你在構造函數中有return語句時,返回的結果將是新表達式的結果。
當構造函數調用getInstance()
時, this
內部指針getInstance()
設置為window
所以this.instance
里面它是一個全局變量window.instance
。
這段代碼只使用一個全局變量window.instance
來跟蹤一個實例,並且可以比它更簡單。
一個更清潔的方法來實現這一點就是這樣。 一個全局實例存儲為函數本身的屬性,而不是頂級全局變量。
function SingletonClass() {
var things = [];
// if there is a previous instance, return it
if (SingletonClass.inst) {
return SingletonClass.inst;
}
// if not called with 'new', force it
if (!this instanceof SingletonClass) {
return new SingletonClass();
}
// remember the first created instance
SingletonClass.inst = this;
// add methods that can see our private `things` variable
this.add = function(thing) {
things.push(thing);
}
this.list = function() {
console.log(things.toString());
}
}
它正在使用window
- getInstance
this
與SingletonClass
的這個不一樣:
function Singleton() {
console.log("This is:", this);
this.instance = null;
function _get() {
console.log("_get's this is:", this);
if (!this.instance) {
console.log("Instance is null");
this.instance = { test: 1 };
}
return this.instance;
}
return _get();
}
var x = new Singleton();
// Output is:
This is:
Singleton
_get's this is:
Window
Instance is null
在JavaScript中實現Singleton模式的更好方法可能是:
function Singleton() {
// Handle not being called with `new`
if (!this instanceof Singleton) {
return new Singleton();
}
var instantiating = !!Singleton.instance,
things = [];
Singleton.instance = instantiating ? this : Singleton.instance;
if (instantiating) {
this.list = function() {
console.log(things.toString());
}
this.add = function(item) {
things.push(item);
}
}
return Singleton.instance;
}
這仍然受到限制。 例如,如果有人用另一個對象替換Singleton.instance
,那么Singleton
不同實例之間的測試可能會中斷 - 例如:
var x = new Singleton();
// Har, har, we be breaking things!
Singleton.instance = {"not": "the", "same": "at", "all": true};
var y = new Singleton();
console.log(x === y); // false
getInstance
函數中的關鍵字this
表示Window
對象。 在SingletonClass
函數中,它表示SingletonClass
對象。
隨着詞匯范圍內, add
和list
功能始終是指相同things
陣列。
這似乎是單身人士的一個很好的聯系:
這是一個與您上面所做的相關的示例:
var myInstance = {
method1: function () {
return "Apple";
},
method2: function () {
return "Orange";
},
banana: "Banana"
};
myInstance.grape = "Grape";
console.log(myInstance.method1(), myInstance.method2(), myInstance.banana, myInstance.grape);
這是工作鏈接:
http://www.quirkscode.com/flat/forumPosts/singleton/singleton.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.