2017-04-09 97 views
0

我以爲我理解了閉包,但突然之間有些簡單的東西似乎沒有奏效。關閉不保持狀態變化

我已經使用閉包創建了一個單例。這裏是一個非常精縮版:

Foobar = function() { 
     var data = null; 

     function init(obj) { 
      data = obj; 
     } 

     return { 
      init: init, 
      currentValue: data 
     } 
    }(); 

既然是單身,我希望,當我稱之爲「初始化」功能,「數據」的值將在所存在的唯一對象改變了,並且該值在使用'currentValue'的後續檢索中仍然可用。

這裏是我的測試:

Foobar.init({id:3}); 
    var test = Foobar.currentValue; 

當我通過這個代碼在Firefox(F12)步驟,我可以看到是 - 「數據」是可見的初始化函數,並且它正在改變當我執行'init'時變量。但是,在退出init函數並返回執行測試的第二行之後,var'test'被賦值爲null(創建單例時的原始值),而不是我傳遞給'init'函數的對象的副本,正如我所料。

有人可以澄清我的困惑嗎?

回答

1

我認爲你混爲一談兩個不同的東西這裏 - 那就是,我覺得你得到「的Javascript通過引用傳遞對象」和「使用Javascript倒閉潮」相混淆。

要回答你的主要問題 - 問題是你在一次實例化中返回變量的值,然後再也不會。所以更新它不會改變任何東西。相反,如果你創建一個函數,返回的價值,你會看到它正在更新:

Foobar = function() { 
 
     var data = null; 
 

 
     function init(obj) { 
 
      data = obj; 
 
     } 
 

 
     function getCurrentValue() { 
 
      return data; 
 
     } 
 
    
 

 
     return { 
 
      init: init, 
 
      getCurrentValue: getCurrentValue 
 
     } 
 
    }(); 
 
    
 
    Foobar.init({id:3}); 
 
    console.log(Foobar.getCurrentValue());

NOW,如果data被作爲對象初始化,您的init只是簡單地以某種方式擴展該對象,那麼屬性本身就會像第一個示例一樣更新,因爲它始終指向整個生命週期中的相同對象:

Foobar = function() { 
 
     var data = {}; 
 

 
     function init(obj) { 
 
      data.id = obj.id; 
 
     } 
 

 
     return { 
 
      init: init, 
 
      currentValue: data 
 
     } 
 
    }(); 
 
    
 
    Foobar.init({id:3}); 
 
    console.log(Foobar.currentValue);

+0

SO說避免謝謝,但在這種情況下......太多次了,所以SO答案專注於解決微型示例或特殊情況下的錯誤,而不回答問題。當它不能幫助下一個人時,他們再次提出問題以獲得對實際問題的一般情況答案,他們會因重複問題而遭到拒絕。你實際上已經回答了這個問題,並且教會了我一些東西,而不是「修正錯誤」,這使得我不太可能在未來重複錯誤。這些答案是我重視SO的原因。 – JackLThornton

1

問題是,將數據放入返回的對象中時,會在函數首次執行時保存值 - 該值爲null。

爲了讀取數據變量的當前值,currentValue必須是一個函數,如init。

Foobar = function() { 
     var data = null; 

     function init(obj) { 
       data = obj; 
     } 

     function currentValue() { 
       return data; 
     } 

     return { 
       init: init, 
       currentValue: currentValue 
     } 
}(); 
console.log(Foobar); 
Foobar.init({id:3}); 
var test = Foobar.currentValue(); 
console.log(Foobar,test); 
1

var Foobar = function() {...}(); 

開始語句可以分解成

var Foobar; 
Foobar = function() {...}(); 

在第一線,全球範圍內註冊的Foobar沒有RHS參考,所以Foobar在Immediately Invoked Functi之前的任何地方都是未定義的表達式(IIFE)上。

在第二行,執行IIFE後,Foobar被分配給IIFE的返回對象,如下所示。

console.log(Foobar); // undefined 
var Foobar = function() {...}(); 
console.log(Foobar); // Object {currentValue: null, init: function} 

的IIFE的在這一點上返回的對象可以想像爲

Click to show Diagram of Initial Foobar

然後,當Foobar.init({ID:3});執行。 Foobar的init會要求提供rhs引用,這恰好是IIFE的初始化。初始化具有閉合對數據和最終設置數據{ID:3}

Click to show Diagram of after Foobar.init

因此

var test = Foobar.currentValue; 

顯然還是一個值作爲它的初始狀態。

爲了達到我們的期望。這裏至少有兩種可能的解決方案,如anied已經提到過。爲了

  1. 創建與關閉IIFE其他功能上數據到 檢索。
  2. 而不是分配數據到一個新的對象,IIFE的init直接 操縱相同的對象本身。
+0

我欣賞這個圖表。你也花時間來回答這個問題,而不是僅僅修復bug。 – JackLThornton