我正在尝试一些示例,并遇到一个问题,如果我们想要向原型添加一个函数,它将无法访问构造函数的私有成员。我遇到了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。这有什么问题吗?
答案 0 :(得分: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 :(得分:2)
我实际上没有对此进行测试,但我认为所有对象都将访问最后一个实例化对象的私有属性。
在每个实例化中,您将原型方法(在所有实例中共享)绑定到要实例化的对象的私有变量:)
答案 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
答案 3 :(得分: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;
}
// namespace 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 closure can call this
function sellItem (item, quantity) {
if (!itemsSold.hasOwnProperty(item)) {
itemsSold[item] = 0;
}
itemsSold[item] += quantity;
}
})();