Hiding Fields in Javascript inheritance like Java, C++, C# and many other programming languages. Why? Please consider following example:
var Base = function() { this.name = "Base"; }; Base.prototype.getName = function() { return this.name; }; var Derived = function() { Base.call(this); this.name = "Drived"; }; Derived.prototype = Object.create(Base.prototype); Derived.prototype.constructor = Derived; Derived.prototype.getName = function() { return this.name; }; var derived = new Derived(); console.log(derived.getName()); // "Derived" console.log(Base.prototype.getName.call(derived)); // "Derived" <-- Basically Unexpected! No??
Now.. I agree that:
- I could wrap up the name field at “getName” definition using self-invoking function.
But that is an overhead for me and I think also for beginners to begin with. Moreover Sharing that field with other function in that class can be a pain
- This is not a practical example.
Sure, yet I fell into problem for this sometime. Actually I was facing this problem when extending a Listener class.
- I could use mangled getter a setter.
Yes, that is going to be my solution. But I don’t want it to do sometime, I want strict to style while being safe. And “being safe” is the part why I am writting here, plus I hope it would help others if is good enough.
Now a simple solution:
var Base = function() { Base.prototype.JSF.call(this).name = "Base"; }; Base.prototype.JSF = function() { if (!this["__mangled_base__"]) this["__mangled_base__"] = {}; return this["__mangled_base__"]; }; Base.prototype.getName = function() { return Base.prototype.JSF.call(this).name + " Moded"; }; var Derived = function() { Base.call(this); Derived.prototype.JSF.call(this).name = "Derived"; }; Derived.prototype = Object.create(Base.prototype); Derived.prototype.constructor = Derived; Derived.prototype.JSF = function() { if (!this["__mangled_derived__"]) this["__mangled_derived__"] = {}; return this["__mangled_derived__"]; }; Derived.prototype.getName = function() { return Derived.prototype.JSF.call(this).name + " Moded"; }; var derived = new Derived(); console.log(derived.getName()); // Derived Moded console.log(Base.prototype.getName.call(derived)); // Base Moded
How would this help?
- Virtually Field Hiding possible this way.
- We can reduce the boilerplate and ease the usability *
- I don’t have to remember mangled name.
- We can make sure mangled name doesn’t cause any conflict *
Here is my simple “Javascript Field – JSF” implementation for your kind review and possible suggestion.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;var $ JSF_INIT = function(t, prfx, tObj) { if (typeof t != "function" || typeof prfx != "string" || typeof tObj != "object") throw Error("Bad Argument(s)"); var $ JSF_REG = $ JSF_INIT.$ JSF_REGISTRY; if ($ JSF_REG[prfx] && t === $ JSF_REG[prfx]) { tObj[prfx] = {}; return; } if (!$ JSF_REG[prfx]) { t.prototype.__$ JSF__ = function() { return this[prfx]; }; $ JSF_REG[prfx] = t; tObj[prfx] = {}; return; } if ($ JSF_REG[prfx] && t !== $ JSF_REG[prfx]) throw Error("Prefix Taken"); }; $ JSF_INIT.$ JSF_REGISTRY = {}; $ JSF = function(context, t) { if (typeof context != "object" || t !== undefined && typeof t != "function") throw Error("Bad Argument(s)"); if (t) return t.prototype.__$ JSF__.call(context); else return context.__$ JSF__(); }; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ var Base = function() { $ JSF_INIT(Base, "_b", this); $ JSF(this).name = ""; }; Base.prototype.getName = function() { return $ JSF(this, Base).name; }; Base.prototype.setName = function(name) { $ JSF(this, Base).name = name; }; var Derived = function() { Base.call(this); $ JSF_INIT(Derived, "_d", this); $ JSF(this).name = ""; }; Derived.prototype = Object.create(Base.prototype); Derived.prototype.construct = Derived; Derived.prototype.getName = function() { var jsf = $ JSF(this, Derived); return jsf.name; }; Derived.prototype.setName = function(name) { $ JSF(this, Derived).name = name; }; var base = new Base(); base.setName("Single Base"); console.log(base.getName()); // Single Base var derived = new Derived(); Base.prototype.setName.call(derived, "Typecasted Base") derived.setName("Derived"); console.log(Base.prototype.getName.call(derived)); // Typecasted Base console.log(derived.getName()); // Derived
Please ask if more information need.
Thanks a lot for your time.