简体   繁体   English

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

[英]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.我正在尝试一些示例并遇到一个问题,如果我们想将 function 添加到原型中,它将无法访问构造函数的私有成员。 I came across this solution.我遇到了这个解决方案。 This seems to be a nice hack.这似乎是一个不错的 hack。

I tried out some other ways and I got the following:我尝试了其他一些方法,得到了以下结果:

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

The solution seems weird because we are adding to the prototype within the constructor function.这个解决方案看起来很奇怪,因为我们在构造函数 function 中添加了原型。 (I haven't seen much of this). (我还没有看到太多这种情况)。 It works on firefox 5 and chrome at least.它至少适用于 firefox 5 和镀铬。 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.您正在做的是每次创建新餐厅 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:更明智的方法是在this上定义它们,这是在构造函数中构造的新 object:

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 :你可以这样做,而不是使用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.缺点是每个新的 object 都会获得所有函数的副本,如果您在构造函数中this添加方法,也会发生这种情况。

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.然后,如果要更改 function 是否为私有,只需在返回的 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:)在每次实例化时,您将原型方法(在所有实例中共享)绑定到正在实例化的 object 的私有变量:)

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:如果你想在构造函数之外给 class 添加方法,你可以使用闭包来保持构造函数的干净:

// 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.我们有时会使用闭包,但仅当您需要在 class 级别管理 state 时。 We use namespaces to manage scope.我们使用命名空间来管理 scope。 For a simple class with prototype methods, we just do this:对于带有原型方法的简单 class,我们只需这样做:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM