2011-08-12 100 views
2

我正在試驗一些例子,並且遇到了一個問題,如果我們想向原型添加一個函數,它將無法訪問構造函數的私有成員。我遇到了this解決方案。這似乎是一個很好的黑客。在構造函數中向原型添加屬性

我嘗試了一些其他的方式,我有以下幾點:因爲我們要添加到構造函數中的原型

var Restaurant = function() 
{ 
    var myPrivateVar; 
    var private_stuff = function() // Only visible inside Restaurant() 
    { 
     return "I can set this here!"; 
    } 
    Restaurant.prototype.use_restroom = function() // use_restroom is visible to all 
    { 
     private_stuff(); 
    } 
    Restaurant.prototype.buy_food = function() // buy_food is visible to all 
    { 
     return private_stuff(); 
    } 
} 
var restaurant = new Restaurant(); 
restaurant.buy_food(); // this would work 
restaurant.private_stuff(); // this won't 

的解決方案似乎不可思議。 (我沒有看到太多這個)。它至少適用於firefox 5和chrome。它有什麼問題嗎?

回答

11

你在做什麼正在重新定義每次你做一個時間上的原型那些方法新的餐廳對象。更理智的方式來做到這將是確定他們this,這是一個構造函數構造新的對象:

var Restaurant = function() 
{ 
    var myPrivateVar; 
    var private_stuff = function() // Only visible inside Restaurant() 
    { 
     return "I can set this here!"; 
    } 
    this.use_restroom = function() // use_restroom is visible to all 
    { 
     private_stuff(); 
    } 
    this.buy_food = function() // buy_food is visible to all 
    { 
     return private_stuff(); 
    } 
} 

你可能只是不喜歡這樣,雖然,沒有使用new

var RestaurantMaker = function() { 
    var myPrivateVar; 
    var private_stuff = function() { 
    return "I can set this here!"; 
    } 

    return { 
    use_restroom: function() { 
     private_stuff(); 
    }, 
    buy_food: function() { 
     return private_stuff(); 
    } 
    }; 
} 

,然後就去做:

var restaurant = RestaurantMaker(); 

這就是所謂的啓迪模塊模式。缺點是每個新對象都會得到所有函數的副本,如果您在構造函數中將方法添加到this也會發生這種情況。

透露出模塊模式(我想讀好一點)的一個非常小的替代版本是這樣的:

var RestaurantMaker = function() { 
    var myPrivateVar; 

    function private_stuff() { 
    return "I can set this here!"; 
    } 

    function use_restroom() { 
    private_stuff(); 
    } 

    function buy_food() { 
    return private_stuff(); 
    } 

    return { 
    use_restroom: use_restroom, 
    buy_food: buy_food 
    }; 
} 

然後,如果你想改變一個函數是否是私人或不是,它是隻需將它從返回的對象中添加或刪除即可。

+1

謝謝你的好解釋和評論。每次創建對象時,我完全錯過了重新定義原型中方法的觀點。我早先看了一下模塊化模式。但是我面臨的問題是,每次我打電話給'restaurantMaker'時,我都會得到'use_restroom'和'buy_food'的新實例。這是不需要的,即如果可以將它們添加到原型中,它們可以被共享。 –

+0

問題是,如果一個函數在與這些私有變量相同的範圍內定義,則該函數只能訪問私有變量。所以如果你只定義了一次函數,他們要麼必須共享相同的私有變量,要麼他們將無法訪問。如果你最終擁有100個餐館對象,這只是一個真正的問題。 – Skilldrick

2

我沒有真正測試這個,但我認爲所有的對象都會訪問最後一個實例化對象的私有屬性。

在要綁定的原型方法(在所有實例共享)對象的私有變量每個實例被實例化:)

2

老實說,它沒有很多道理給我。當然,你可以通過這種方式調用你的私有函數,但它並不能解決最初的問題 - 也就是說,你仍然需要在構造函數中添加方法。

如果你想方法添加到類的構造函數外,你可以使用閉包,以保持清潔的構造:

// Creating a closure inside a self-calling function 
var Restaurant = (function() { 

    // Only visible inside this closure 
    var myPrivateVar; 
    var private_stuff = function() { 
     return "I can set this here!"; 
    } 

    var Restaurant = function() {}; 

    // use_restroom is visible to all 
    Restaurant.prototype.use_restroom = function() { 
     private_stuff(); 
    }; 

    // buy_food is visible to all 
    Restaurant.prototype.buy_food = function() { 
     return private_stuff(); 
    }; 

    // We give back the Restaurant-constructor to the people 
    return Restaurant; 

})(); 

var restaurant = new Restaurant(); 
restaurant.buy_food(); // this would work 
restaurant.private_stuff(); // this won't 
+1

而是在全球範圍內定義'Restaurant',然後分配給它的,你可以只說'window.Restaurant'瓶蓋內。 – Skilldrick

+0

是的,你是對的,它甚至可以從自調用函數返回,所以它可以分配給任何變量。 –

+2

這裏的問題在於'Restaurant'的所有實例都會共享相同的'privateVar',這可能是或者可能不是OP的內容。 – Skilldrick

0

我們採取不同的方法。我們有時會使用閉包,但只有在需要在班級級別管理狀態時纔會使用閉包。我們使用命名空間來管理範圍。對於原型方法的簡單類,我們只要做到這一點:

/** 
* @namespace 
*/ 
var chain = {}; 

(function() { 

    /** 
    * The constructor is used to manage private data 
    * @constructor 
    */ 
    chain.Restaurant = function() { 
     // Only visible inside this constructor 
     var inventory = { }; 

     /** 
     * add an item with a count to the inventory 
     * This is a privileged function. 
     * @param {String} item The identifier for the item you are adding 
     * @param {String} count The count you are adding for the item. 
     */ 
     this.addInventory = function (item, count) { 
      if (count < 0) { 
       // throw an error 
      } 
      var current = this.getInventory(item); 
      inventory[item] = current + count; 
     } 

     // privileged function 
     this.getInventory = function (item) { 
      if (inventory.hasOwnProperty(item)) { 
       return inventory[item]; 
      } 
      return 0; 
     } 

     // privileged function 
     this.removeInventory = function (item, count) { 
      throwIfNegative(count); 
      if (this.getInventory(item) < count) { 
       throw new Error("Inventory Unavailable"); 
      } 
      inventory[item] -= count; 
     } 

     // private function, only visible to the privileged functions 
     function throwIfNegative (value) { 
      if (value < 0) { 
       throw new Error("Negative Inventory Is Not Valid"); 
      } 
     } 
    } 

    // member/prototype method 
    chain.Restaurant.prototype.sellInventory = function (item, count) { 
     var availabe = this.getInventory(item); 
     var sellCount = Math.min(available, count, 0); 
     if (sellCount > 0) { 
      // do this conditionally if there are implications to invoking the functions 
      this.removeInventory(sellCount); 
      sellItem(item, sellCount); 
     } 
     return sellCount; 
    } 

    // member/prototype method 
    chain.Restaurant.prototype.hasInventory = function (item, count) { 
     return this.getInventory(item) >= count; 
    } 

    // member/prototype method 
    chain.soldQuantity = function (item) { 
     if (!itemsSold.hasOwnProperty(item)) { 
      return 0; 
     } 
     return itemsSold[item]; 
    } 

    // functions defined in this closure can see this 
    var itemsSold = { }; 

    // all functions defined in this closer can call this 
    function sellItem (item, quantity) { 
     if (!itemsSold.hasOwnProperty(item)) { 
      itemsSold[item] = 0; 
     } 
     itemsSold[item] += quantity; 
    } 
})(); 
相關問題