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

问题描述 投票:2回答:4

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

javascript constructor prototype-programming
4个回答
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 }; } 添加方法,也会发生这种情况。

一个非常小的替代版本的揭示模块模式(我认为读得更好)看起来像这样:

// 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

然后,如果要更改函数是否为私有,则只需在返回的对象中添加或删除它即可。


2
投票

我实际上没有对此进行测试,但我认为所有对象都将访问最后一个实例化对象的私有属性。

在每个实例化中,您将原型方法(在所有实例中共享)绑定到正在实例化的对象的私有变量上:)


2
投票

老实说,这对我来说没有多大意义。当然,您可以通过这种方式调用私有函数,但它无法解决最初的问题 - 也就是说,您仍然需要在构造函数中添加方法。

如果要在构造函数外部的类中添加方法,可以使用闭包来保持构造函数的清洁:

/**
 * @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;
    }
})();

0
投票

我们采取不同的方法。我们有时会使用闭包,但只有在需要在类级别管理状态时才会使用闭包。我们使用命名空间来管理范围。对于使用原型方法的简单类,我们只需执行以下操作:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.