1

Three-part question:

  1. Any value to adding a second layer of abstraction, and using a prototype getter/setter function to invoke privileged constructor getter/setter functions? See ns.Wheel.prototype.getWeight2() and ns.Wheel.prototype.setWeight2() below.

  2. The call to swiss.getWeight() invokes the this.getWeight() method in the constructor. Any way to move one level up the prototype chain to instead invoke ns.Wheel.prototype.getWeight()?

  3. Any value to "hiding" the prototype getter/setter functions "behind" the constructor getter/setters? E.g., ns.Wheel.prototype.getWeight() is "hidden" behind the this.getWeight() method in the constructor.

Also note how the prototype getter/setters add the unit for grams; i.e., "g" unit. E.g., this.getWeight returns 1000, while ns.Wheel.prototype.getWeight returns 1000g.

Wheels of swiss cheese are used in this example.

(function(ns) {
    ns.Wheel = function() {
        var _weight = 1000; // weight of cheese wheel. Private variable.
        this.getWeight = function() { return _weight } // privileged weight getter
        this.setWeight = function(weight) { return _weight = weight } // privileged weight setter
    }
    ns.Wheel.prototype.getWeight = function() { return this.getWeight()+'g' }
    ns.Wheel.prototype.setWeight = function(weight) { return this.setWeight(weight)+'g' }
    ns.Wheel.prototype.getWeight2 = function() { return this.getWeight()+'g' }
    ns.Wheel.prototype.setWeight2 = function(weight) { return this.setWeight(weight)+'g' }
})(window.cheese = window.cheese || {});  // immediate function namespacing technique

var swiss = new cheese.Wheel();
console.log(swiss.getWeight()); //-> 1000. Invokes constructor method
console.log(swiss.setWeight(2000)); //-> 2000. Invokes constructor method
console.log(swiss._weight); //-> undefined. Private variable!!!
console.log(swiss.getWeight2()); //-> 2000g. Invokes prototype method.
console.log(swiss.setWeight2(9000)); //->9000g. Invokes prototype method.
kmiklas
  • 13,085
  • 22
  • 67
  • 103
  • 1
    I'd recommend having a look at `prototype.js` and its relly good [inheritance behavour](http://prototypejs.org/doc/latest/language/Class/create/index.html) using `Class.create` & `$super()` for your purpose. Now stone me to death for reanimating such an evil, old library, friends! ;) – RienNeVaPlu͢s Dec 16 '13 at 21:12
  • 3
    I would recommend not worrying about making variables "private" in JS. In order to have "private" variables, you have to move your methods that use them into your constructor, which means that you have a copy of every non-"static" method in your constructor. This is a waste of memory. A common convention is to name "private" variables with underscores and then just not ever use variables with underscore prefixes outside of the context in which they were created. If you really want privates, consider using TypeScript, which does what I've suggested but does checks for you at compile time. – John Kurlak Dec 16 '13 at 21:16
  • I agree with the comments not to use closures to have "private" members and privileged methods but if you really have to do it then this pattern may be of use: http://stackoverflow.com/a/19879651/1641941 It doesn't prevent you from accessing private values completely but if you have a lot of "private" values it will decrease the number of members on your object named `_somePrivate` – HMR Dec 16 '13 at 23:56

2 Answers2

4
  1. Any value to adding a second layer of abstraction, and using a prototype getter/setter function to invoke privileged constructor getter/setter functions?

    In general, I would say no. It's much more memory efficient to have method exist on the prototype when possible, since methods set during instance construction will exist on each and every instance, while prototype methods need only be defined once, ever.

  2. Any way to move one level up the prototype chain to instead invoke ns.Wheel.prototype.getWeight()

    If you want some method to have "private" access to variables defined inside of the constructor, those function must also be defined inside the constructor. Function scope is set at declaration time. Therefore, there is no way for a function defined outside of the constructor (e.g., a prototype method) to have access to constructor-scope variables.

    Because JavaScript functions are first-class objects, you can invoke the prototype method with call or apply: ns.Wheel.prototype.setWeight.call(this, 1000) (where the first argument sets the this value for the function being invoked, and further arguments are actual function arguments).

  3. Any value to "hiding" the prototype getter/setter functions "behind" the constructor getter/setters?

    In your case, ns.Wheel.prototype.getWeight (and its corresponding setter) are never used. When getWeight2 calls this.getWeight(), that resolves to the instance-level function, and there is no need to look up the prototype chain for the prototype method.

    The only value I can see might be if you conditionally shadowed some prototype method with an instance method, and in other cases let the prototype method show through. However, if you want that prototype method to have access to constructor-scope variables, you're out luck, as explained in point #2. For example:

    function Egg(color) {
        if(color == "invisible") {
            this.examine = function() { return "You can't see the egg at all! }
        } 
    }
    
    Egg.prototype.examine = function() { return "The egg is painted " + color; }
    

    This isn't a great example (it works better with simple values like numbers, instead of functions), but it's the only "useful" example of prototype shadowing I could think of.

I strongly endorse the second comment above, which advises against using private variables at all. They waste memory, as described in my first point, by requiring an method that uses them to be defined on every instance. By trying to simulate private variables, you squander the power of prototypal inheritance. Instead (as mentioned in the comments), make all your variables public and designate your "private" variables with a leading underscore.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • 1. Agreed... 2. That's what **this.getWeight()** and **this.setWeight** are... 3. That's my question #2: how can I "let the prototype method show through?" Thx for response :^) – kmiklas Dec 16 '13 at 21:21
  • @kmiklas I misunderstood your point #2; I've edited extensively. – apsillers Dec 16 '13 at 21:34
1

Any value to adding a second layer of abstraction, and using a prototype getter/setter function to invoke privileged constructor getter/setter functions? See ns.Wheel.prototype.getWeight2() and ns.Wheel.prototype.setWeight2() below.

Why not? Well, keep in mind that calling functions is expensive, so if it's only to add 'g' at the end you can consider doing it manually if you want to gain some microseconds.

The call to swiss.getWeight() invokes the this.getWeight() method in the constructor. Any way to move one level up the prototype chain to instead invoke ns.Wheel.prototype.getWeight()?

Yes, you can:

  • Use different names
  • Use cheese.Wheel.prototype.getWeight.call(swiss)

Any value to "hiding" the prototype getter/setter functions "behind" the constructor getter/setters? E.g., ns.Wheel.prototype.getWeight() is "hidden" behind the this.getWeight() method in the constructor.

It depends on the situation. In your case I would use different names to avoid "hiding".

Oriol
  • 274,082
  • 63
  • 437
  • 513