在构造函数中向原型添加属性

时间:2011-08-12 10:47:51

标签: javascript constructor prototype-programming

我正在尝试一些示例,并遇到一个问题,如果我们想要向原型添加一个函数,它将无法访问构造函数的私有成员。我遇到了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。这有什么问题吗?

4 个答案:

答案 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;
    }
})();