Brett's Blog
Edit     New

Saturday, January 10, 2009

True Private Instance Variables (and Methods) in JavaScript

[Additon: Feb. 19, 2009]: I now recommend this approach instead.

[Addition: Jan. 12, 2008: Notice how, despite this approach being slightly cumbersome, it may be the most frequent need and recommended case for adding privacy--having private instance variables--as it is usually the data which must be kept private (and which varies per object) and which is often recommended to be always accessed through public methods which allow for potential future further changes in processing before handing over (or setting) the data.]

I came across this very clever method of giving true private instance methods to JavaScript (which did not require (publicly-accessible and constructed-on-each-instance) privileged methods). One thing his approach does not deal with, however, is private instance variables. (Note that we're not referring to private (static) variables which are the same across each instance and created in a closure; we're talking about genuine (private) instance variables.) Where he says in his code that "private stuff" can go, besides his private instance methods (which are really private static methods cleverly used as private instance methods by the call() method and which are handling public instance variables), only private static variables can go, and not private instance variables.

My approach that follows (which builds on his) has a few disadvantages of which I'm aware:
  1. It requires some unsightly code in places (albeit just one pasteable line in a constructor and , for convenience, a pasteable line for each public method that wishes to use a private instance variable with shorthand syntax)

  2. It requires one public instance variable (an integer)--an incrementor for each instance (and a single private static iterator and private static container of instance objects across all instances) ; if this instance variable is altered (just as is usually the case with a privileged method being altered), the functioning of any derived object (including previously instantiated ones) can be compromised.1 However, if all consumers of the code are trusted, it should not be too difficult to enforce a no-external-access policy for any obj._$ (this is not an issue for front-end users unless your code uses insecure methods like eval() or the like).


Its advantages:
  1. There will only be one set of private instance methods produced across all instances (contrast this with potentially multiple (cumbersome and high memory) privileged functions added upon each instance in order to serve as a bridge to the prototype for the private instance variables in the constructor; our approach here doesn't need any such bridge (though it does produce an object for each instance to hold the private instance variables).

  2. Allows private variables and methods to be shared across constructor and public methods; the instance variables (and methods) are available everywhere in the class (constructor, and private and public instance methods) if accessed through __[this._$], or _. if a shortcut line is added to each method where private instance variables will be used)

  3. It operates transparently without need for any helper functions or prototype overloading, etc.




var SomeClass = function () {
var privStatic; // Not being used here
function privateStaticMethod () {} // Not being used here

// Private instance holder (will be used as such)
var _$ = 0; // Our private static incrementor (increment once per instantiation to distinguish objects)
var __ = []; // Each index on this array will be an object holding private instance variables, with each index corresponding to a unique id generated by each constructor and stored on each of its objects; see also note 2 below

// Private instance methods (to be used as such below)
function privInstMethod (instName) {
return __[this._$][instName];
}

function Constructor () {
this._$=_$; _$++; var _=__[this._$]={}; // Copy this line for each constructor

// Private instances code
_.privInst = 5;
}
Constructor.prototype.someMethod = function () {
var _=__[this._$]; // copy this line for each method which uses a private instance variable (to have a more convenient shortcut)

_.privInst++;
};
Constructor.prototype.getMethod = function () {
var _=__[this._$];

return _.privInst;
};
Constructor.prototype.anotherGetMethod = function () {
return privInstMethod.call(this, 'privInst'); // Call a private instance method as per Andrea Giammarchi's approach (i.e., treat a private static method as a private instance one); only the first argument is always required, the remaining arguments are the real arguments passed to the private instance method; we could also define, as he did, a shortcut, but that is defining an additional public method
};

return Constructor;
}();

var cl = new SomeClass();
cl.someMethod();

cl.someMethod();
alert(cl.getMethod()); // 7

var cl2 = new SomeClass();
// cl2._$=0; // The biggest down-side of our approach is that things can really get messed up if consumer code does something like this (or if an inheriting class similarly tampers with the variable), but at least there is only one such variable to mess up, making it less likely to happen (and it is not named in such a way as for most trusted consumers to be tempted to alter it), and does not provide access to the private members
cl2.someMethod();
alert(cl2.getMethod()); // 6

alert(cl.getMethod()); // still 7

alert(cl.anotherGetMethod()); // also 7
alert(cl2.anotherGetMethod()); // also 6


NOTES

  1. One could replace the first line in the constructor with this:


    this._$= function get_$ (_$) { return function () {return _$}}(_$); _$++; var _=__[get_$()]={}; // Copy this line for each constructor


    and then replace all other instances of "this._$" in the code with "this._$()" except for within the constructor, where you can use get_$().

    This would add one privileged method to each instance (but only one--not one for each private variable) and be slightly more unattractive. An advantage of this would be that the function used internally within the constructor would be secure for previously instantiated objects (resetting obj._$() externally would not affect it), but it would only be secure within the constructor--not within its public methods (as would be the case for any privileged method).

  2. The line "var __ = [];" might be divided into protected and private holders to allow inheriting classes to share access by passing in an object to this anonymous function (or create a global storage object for shared "protected" instance variables (or protected static ones)). I aim to discuss this possibility (as for a different approach to protected methods) in a future post.

Friday, January 09, 2009

The World's Most Dangerous Drug: Nationalism ?

Among the world's top most dangerous drugs--nationalism... (We might also take racism as being akin to nationalism...)

This drug's most obvious effect is (selective) blindness. The addict will filter and process everything through the distorted vision caused by this drug. Only the addict (and by extension his country) will be right in all situations, and others are suspect of blame, weakness, aggression, etc.

As with drug addiction, the addict will most likely vigorously deny the dependence, but it will be clear from their actions that, for the most part, they will only be able to place blame on others (other nations or people in other countries) and not take account for their own actions (often because this is indeed more difficult to do). While fault may indeed be found with others (or other countries and their people), this is often used as an excuse for failing to address and overcome the system of mutually enabling behavior which affects both addicts in their relation to one another--such as an unjust or weak international order cannot resolve disputes adequately and leads to further national animosities (and to look at one's own faults (and one's own country) in the process).

Here's a twelve-step program (based largely on the original one for alcoholics) to overcoming this dependence... They apply equally no matter your country of origin...

Note that, needless to say, none of this is professional psychiatric advice.
  1. We admitted we were powerless over nationalism—that our lives (and those of our fellow national and world citizens, and our international relations as countries) had become unmanageable.

  2. Came to believe that a Power (or at a minimum, a federated world power) greater than ourselves (and greater than our country alone) could restore us to sanity.

  3. Made a decision to turn our will and our lives over to the care of God as we understood Him (or at least to a higher representative authority which encompassed all people).

  4. Made a searching and fearless moral inventory of ourselves (including ourselves as national citizens). [One of the most important and difficult steps, in my opinion - B.Z.]

  5. Admitted to God, to ourselves, and to another human being the exact nature of our wrongs as an individual and national citizen. [I don't really agree with the part about admitting it to another human being as far as small offenses by individuals, as I don't believe in humiliation before another human being, but I do believe disclosure may be truly needed in some larger cases for therapy or justice (e.g., like in South Africa's Truth and Reconciliation Commission or even payment of reparations).]

  6. Were entirely ready to have God remove all these defects of character in ourselves and our country (and/or let a justly constituted international system of governance by the people of the world monitor and prescribe remedies for serious defects not handled adequately by our own national authorities).

  7. Humbly asked Him to remove our shortcomings as individuals and as a country. [Note that, as with addiction recovery, one need not focus blame excessively on oneself as a country or people, and there are advantages to taking note of steps toward progress, but without letting such pride or contentment degenerate into laxity or regression, or ignoring the need (as discussed in the next steps) to redress serious wrongs one (or one's country) has committed. - B.Z.]

  8. Made a list of all persons, peoples, and nations we had harmed, and became willing to make amends to them all.

  9. Made direct amends to such people or nations wherever possible (as an individual or country), except when to do so would injure them or others.

  10. Continued to take personal inventory and when we (as individuals or as a country) were wrong promptly admitted it.

  11. Sought through prayer and meditation to improve our conscious contact with God as we understood Him (or at least seek to improve our conscious knowledge of, participation in, reformation of, and adherence to His current higher legal body for humanity, the United Nations and other international institutions), praying only for knowledge of His Will for us (or at least for knowledge of international opinion and consensus, as God favors unity except where unity is truly amoral, as in Hitler's Germany) and the power to carry that out (or at least that a justly constituted United Nations, comprised of our nation and others, will be given the authority and power by God to act as the executive for such consensus and act as the global policemen to adequately defend individual and national liberties as necessary, as our world so sorely needs).

  12. Having had a spiritual awakening as the result of these steps, we tried to carry this message to other nationalists, and to practice these principles in all our affairs (as individuals or as a nation).

[Updated 4/26/2011: Added a little extra content for step 6.]


Google
 
Brett's Blog Web