开发者

Adding properties to a prototype within the constructor function

I was experimenting with some examples and came across a problem that if we want to add a function to a prototype it will not be able to access the private members of the constructor. I came across this solution. This seems to be a nice hack.

I tried out some other ways and I got the following:

var Restaurant = function()
{
    va开发者_StackOverflowr 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

The solution seems weird because we are adding to the prototype within the constructor function. (I haven't seen much of this). It works on firefox 5 and chrome at least. Is there something wrong with it?


What you're doing is redefining those methods on the prototype every time you make a new restaurant object. The more sane way to do that would be to define them on this, which is the new object being constructed in a constructor:

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

You could just do it like this though, and not use 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();
    }
  };
}

and then just do:

var restaurant = RestaurantMaker();

This is called the revealing module pattern. The downside is that each new object gets a copy of all the functions, which also happens if you add methods to this in your constructor.

A very small alternative version of the revealing module pattern (which I think reads a bit better) looks like 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
  };
}

Then, if you want to change whether a function is private or not, it's just a matter of adding or removing it from the returned object.


I didn't actually test this, but I think all the objects would access to the last instantiated object's private properties.

On each instantiation you're binding the prototype methods (shared across all instances) to the private variables of the object being instantiated :)


Honestly, it doesn't make a lot of sense to me. Sure, you can have calls to your private functions this way, but it doesn't solve the initial problem - that is, you still need to add methods inside the constructor.

If you want to add methods to the class outside the constructor, you can use closures to keep constructors clean:

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


We take a different approach. We do use closures sometimes, but only when you need to manage state at the class level. We use namespaces to manage scope. For a simple class with prototype methods, we just do this:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜