2009-12-13 71 views
19

我剛剛在javascript中討論單身設計的幾個主題。我對設計模式的東西100%是新的,但正如我所看到的,因爲根據定義,Singleton不需要實例化,在概念上如果它不被實例化,我認爲它不必像從藍圖(類)創建的傳統對象。所以我的奇蹟是,爲什麼不把單身人士想象成靜態可用的東西,而是包含在某種範圍內,應該是全部。javascript單身問題

從我看到了一線,他們大多做一個單身,雖然傳統的JavaScript,然後做一個僞構造

new function(){} 

嗯,我只是覺得一個對象字面夠不夠:

var singleton = { 
    dothis: function(){}, 
    dothat: function(){} 
} 

吧?或有人獲得更好的見解?

[更新]:我的觀點再次強調爲什麼人們不會只用一種簡單的方法在javascript中創建單例,就像我在第二段中展示的那樣,如果有絕對的理由請告訴我。我通常害怕這種情況,我簡化了很多東西:D

+2

'dothis'和'dothat'提醒我波拉特 – 2010-06-11 18:02:15

回答

31

我同意你的觀點,最簡單的方法是使用對象文本,但如果你想私人的成員,你可以實現服用closures優勢:

var myInstance = (function() { 
    var privateVar; 

    function privateMethod() { 
    // ... 
    } 

    return { // public interface 
    publicMethod1: function() { 
     // private members can be accessed here 
    }, 
    publicMethod2: function() { 
     // ... 
    } 
    }; 
})(); 

關於new function(){}結構,它會只需使用匿名函數作爲constructor function,該函數內的上下文將成爲將返回的新對象。

編輯:在應對@J5's comment,說起來很簡單的事情,其實我覺得,這可能是爲使用懶惰函數定義模式一個很好的例子:

function singleton() { 
    var instance = (function() { 
    var privateVar; 

    function privateMethod() { 
     // ... 
    } 

    return { // public interface 
     publicMethod1: function() { 
      // private members can be accessed here 
     }, 
     publicMethod2: function() { 
     // ... 
     } 
    }; 
    })(); 

    singleton = function() { // re-define the function for subsequent calls 
    return instance; 
    }; 

    return singleton(); // call the new function 
} 

當功能被稱爲第一次,我做對象實例,並重新分配singleton到一個新的函數,該函數在它的閉包中具有該對象實例。

在第一次通話結束之前,我執行重新定義的singleton函數,該函數將返回創建的實例。

singleton函數的以下調用將簡單地返回存儲在它的閉包中的instance,因爲新函數是將要執行的函數。

可以證明,通過比較對象返回:

singleton() == singleton(); // true 

==操作的對象將返回true只有兩個操作數的對象參考是一樣的,它會返回false即使對象是相同的,但它們是兩個不同的實例:

({}) == ({}); // false 
new Object() == new Object(); // false 
+0

@CMS:私有成員做在做一個單身感,不能用簡單的對象字面來實現。謝謝。 – Shawn 2009-12-13 06:09:22

+0

@CMS:我想那是道格拉斯的代碼。對於單身定義來說,這只是我不必要的往返行程。我覺得封閉已經夠好了。 – Shawn 2009-12-13 06:12:04

+0

@Shawn:是的,我提到,不僅是因爲幾乎所有的對象實例可以被複制,打破了*「單身」的概念,* ... – CMS 2009-12-13 06:25:34

0

後面的代碼框顯示了我所看到的JS開發人員在Javascript中調用他們的OO設計版本。

Singetons意味着是無法構建(除非,我想奇異的物體,在最初的定義,你有一個單獨的一個,全局實例。

+0

的第二個例子是一個對象,而不是一個函數,因此它不能與'new'使用。 – Wyzard 2009-12-13 05:35:26

+0

@Isaac:我不認爲你可以在第二個例子中使用新的。關鍵是我認爲var singleton = {}代碼應該是一個更好和更自然的單例聲明。 – Shawn 2009-12-13 05:38:38

+0

感謝您的澄清!我自己還在接觸JS。我會編輯這些廢話。 – Isaac 2009-12-13 05:41:19

1

我想知道這個太,但是定義一個有函數的對象似乎是合理的,沒有意義創建一個沒有人應該調用的構造函數,當你可以直接定義對象時創建一個沒有原型的對象。如果你想讓你的單身人士成爲一些現有的「班級」的實例 - 也就是說,你希望它有一些其他的對象作爲其原型 - 那麼你需要使用構造函數,以便在調用它之前可以設置它的prototype屬性。

+0

我認爲原型仍然工作在我的第二個例子,只是爲了添加singleton.prototype = someOtherObject;因爲在javascript中實際上沒有類作爲類,所以人們稱之爲僞類,從某種意義上說,它們實際上沒有區別於字面聲明的類。 – Shawn 2009-12-13 05:47:09

+0

'prototype'屬性只適用於函數(構造函數)。對象具有引用構造函數的'prototype'對象的內部'[[Prototype]]'屬性。要更改原型,需要引用構造該對象的函數。 – 2009-12-13 06:02:15

+0

@Matthew:謝謝。 :d – Shawn 2009-12-13 06:15:51

0

使用「僞構造函數」的要點是,它創建了一個新的變量範圍。您可以在函數內部聲明局部變量,這些變量可以在任何嵌套函數中使用,但不能在全局範圍內使用。

實際上有兩種方法。你可以像new那樣調用函數,或者直接調用函數。你將如何編寫代碼存在細微的差異,但它們基本上是等價的。

你的第二個例子可以寫成這樣:

var singleton = new function() { 
    var privateVariable = 42; // This can be accessed by dothis and dothat 

    this.dothis = function() { 
     return privateVariable; 
    }; 

    this.dothat = function() {}; 
}; // Parentheses are allowed, but not necessary unless you are passing parameters 

var singleton = (function() { 
    var privateVariable = 42; // This can be accessed by dothis and dothat 

    return { 
     dothis: function() { 
      return privateVariable; 
     }, 

     dothat: function() {} 
    }; 
})(); // Parentheses are required here since we are calling the function 

你也可以傳遞參數要麼功能(你需要括號添加到第一個例子)。

+0

@Matthew:謝謝。你的第二個例子看起來更接近我。從語法本身來看,它更貼近「單身」概念。第一個對我來說看起來有點不自然,因爲它實際上是一個被稱爲「單身」的傳統對象。感謝您的澄清。 – Shawn 2009-12-13 06:07:02

+0

@Mthethew ...這個構造對我來說看起來像一個閉包。以後的代碼無法獲取除現有對象引用之外的單例。 如果該對象引用被關閉,那麼使用來自不同作用域的單例將是100%不可能的。 – 2009-12-13 14:55:56

+1

@ J5:這是封閉。我不確定你最後提到的是什麼。你在說「單身」嗎?只要你在全球範圍內聲明它,你就可以在任何地方使用它。 – 2009-12-13 18:19:00

3

我已經使用第二版(var singleton = {};)從Firefox擴展到網站的所有內容,它的工作非常好。一個好主意是沒有定義的大括號裏面的東西,但使用以外它的名字的對象,像這樣:

var singleton = {}; 
singleton.dothis = function(){ 

}; 
singleton.someVariable = 5; 
+0

你正在使用對象文字符號的'自由風格'。相同的東西。我個人也認爲這是最具吸引力的。可能因爲它不能更簡單,而且看起來非常自然。你不覺得嗎? :D – Shawn 2009-12-13 06:14:44

+0

加上似乎只有在JavaScript你可以做到這一點。這是一個保佑。 – Shawn 2009-12-13 06:17:22

+1

即使單身人士從未使用過,這是不是會聲明一堆方法? – EricG 2012-10-10 13:12:47

1

的單例模式是通過創建一個新實例的方法創建一個類實現如果不存在的話。如果一個實例已經存在,它只是返回對該對象的引用。 1

(function (global) { 

    var singleton; 

    function Singleton() { 
     // singleton does have a constructor that should only be used once  
     this.foo = "bar"; 
     delete Singleton; // disappear the constructor if you want 
    } 

    global.singleton = function() { 
     return singleton || (singleton = new Singleton()); 
    }; 

})(window); 

var s = singleton(); 
console.log(s.foo); 

var y = singleton(); 
y.foo = "foo"; 
console.log(s.foo); 

你不只是申報單爲對象,因爲它實例化,它沒有聲明。它也沒有提供一個不知道以前引用單例來檢索它的代碼的機制。單例不是單例返回的對象/類,它是一個結構。這類似於閉包變量不是閉包,提供閉包的函數範圍是閉包。

+0

刪除構造函數並沒有真正幫助避免再次使用構造函數,因爲對構造函數的引用在已創建的對象上可用,例如:'var z = new y.constructor();' – CMS 2009-12-13 18:19:09

+0

是的......但它的東西;) – 2009-12-14 18:47:25

0

如何:

function Singleton() { 

    // --------------- 
    // Singleton part. 
    // --------------- 

    var _className = null; 
    var _globalScope = null; 

    if (!(this instanceof arguments.callee)) { 
     throw new Error("Constructor called as a function."); 
    } 

    if (!(_className = arguments.callee.name)) { 
     throw new Error("Unable to determine class name.") 
    } 

    _globalScope = (function(){return this;}).call(null); 

    if (!_globalScope.singletons) { 
     _globalScope.singletons = []; 
    } 

    if (_globalScope.singletons[_className]) { 
     return _globalScope.singletons[_className]; 
    } else { 
     _globalScope.singletons[_className] = this; 
    } 

    // ------------ 
    // Normal part. 
    // ------------ 

    var _x = null; 

    this.setx = function(val) { 
     _x = val; 
    }; // setx() 

    this.getx = function() { 
     return _x; 
    }; // getx() 

    function _init() { 
     _x = 0; // Whatever initialisation here. 
    } // _init() 
    _init(); 

} // Singleton() 

    var p = new Singleton; 
    var q = new Singleton; 

    p.setx(15); 
    q.getx(); // returns 15 
0

我偷了這個從CMS/CMS' answer,並將其更改爲可以調用爲:

MySingleton.getInstance().publicMethod1(); 

隨着輕微的交替:

var MySingleton = {    // These two lines 

    getInstance: function() {  // These two lines 

     var instance = (function() { 

     var privateVar; 

     function privateMethod() { 
      // ... 
      console.log("b"); 
     } 

     return { // public interface 
      publicMethod1: function() { 
       // private members can be accessed here 
       console.log("a"); 
      }, 
      publicMethod2: function() { 
      // ... 
      privateMethod(); 
      } 
     }; 
     })(); 

     singleton = function() { // re-define the function for subsequent calls 
     return instance; 
     }; 

     return singleton(); // call the new function 
    } 
} 
3

的ES5規範允許我們使用的Object.create():

var SingletonClass = (function() { 
    var instance; 
    function SingletonClass() { 
     if (instance == null) { 
      instance = Object.create(SingletonClass.prototype); 
     } 

     return instance; 
    } 

    return { 
     getInstance: function() { 
      return new SingletonClass(); 
     } 
    }; 
})(); 

var x = SingletonClass.getInstance(); 
var y = SingletonClass.getInstance(); 
var z = new x.constructor(); 

這是很好的,因爲我們並不擔心我們的構造泄漏,我們仍然總是以相同的實例結束。

這種結構也具有直到需要我們的辛格爾頓不建立自己的優勢。另外,我們在這裏使用閉包來防止外部代碼無意中或以其他方式使用我們的「實例」變量。我們可以在同一個地方構建更多的私有變量,我們可以定義任何我們關心的公共導出到我們的類原型上的東西。