Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Private Properties in MooTools 1.3+ Classes

I've spent the last couple days researching a way to have private or protected properties in MooTools classes. Various articles (ie, Sean McArthur's Getting Private Variables in a MooTools Class) provide an approach for deprecated versions of MooTools, but I haven't been able to track down a working method for MooTools 1.3+.

Today, after playing with code for hours, I think I have have created a suitable solution. I say "think," because I'm really not that experienced as a programmer. I was hoping the community here could check out my code and tell my if it's actually a valid solution, or a hackjob emulation.

var TestObj = (function() {

 var _privateStaticFunction = function() { }

 return new Class({

  /* closure */
  _privates: (function() {

   return function(key, val) {

    if (typeof(this._data) == 'undefined') this._data = {};

    /* if no key specified, return */
    if (typeof(key) == 'undefined') return;

    /* if no value specified, return _data[key] */
    else if (typeof(val) == 'undefined') {
      if (typeof(this._data[key]) != 'undefined') return this._data[key];
      else return;
    }

    /* if second argument, set _data[key] = value */
    else this._data[key] = val;

   }

  /* tell mootools to hide function */
  })().protect(),

  initialize: function() {},

  get: function(val) { return this._privates(val); },
  set: function(key,val) { this._privates(key,val); }

 })

})();


obj1 = new TestObj();
obj2 = new TestObj();

obj1.set('theseShoes','rule');
obj2.set('theseShoes','suck');

obj1.get('theseShoes') // rule
obj2.get('theseShoes') // suck
obj1._privates('theseShoes') // Error: The method "_privates" cannot be called
obj1._privates._data // undefined
obj1._privates.$constructor._data // undefined

I really appreciate any tips! Thanks, everyone!

EDIT: Well, this is embarrassing. I forgot to check out the obvious, obj1._data. I didn't think the this would reference the instance object! So, I suck. Still, any ideas would be awesome!

like image 293
moon prism power Avatar asked Oct 10 '22 23:10

moon prism power


1 Answers

heh. in your case, a simpler pattern would do the trick.

consider a var behind a closure - extremely hard to puncture. it is available through the getter and setter.

downside: data values cannot be in the instance or they can be accessed directly.

var testObj = (function() {
    var data = {__proto__:null}; // 100% private

    return new Class({
        get: function(key) {
            return data[this.uid][key] || null;
        },
        set: function(key, value) {
            data[this.uid][key] = value;
        },
        remove: function(key) {
            delete data[this.uid][key];
        },
        otherMethod: function() {
            alert(this.get("foo"));
        },
        initialize: function() {
            this.uid = String.uniqueID();
            data[this.uid] = {};
        }
    });
})(); // why exec it?


var foo = new testObj();
var bar = new testObj();

foo.set("bar", "banana");
console.log(foo.get("bar")); // banana!
console.log(bar.get("bar")); // undefined.
bar.set("bar", "apple");
console.info(foo.get("bar"), bar.get("bar")); // banana apple

In action: http://jsfiddle.net/dimitar/dCqR7/1/

I am struggling to find a way to puncture this pattern at all - which is sometimes achievable through prototyping like this.

in fact, i played with it some and here's the fixed pattern w/o the namespacing:

http://jsfiddle.net/dimitar/dCqR7/2/

var testObj = (function() {
    var data = {__proto__:null}; // 100% private

    return new Class({
        get: function(key) {
            return data[key] || null;
        },
        set: function(key, value) {
            data[key] = value;
        },
        remove: function(key) {
            delete data[key];
        },
        otherMethod: function() {
            alert(this.get("foo"));
        }
    });
});


var foo = new new testObj();
var bar = new new testObj();

foo.set("bar", "banana");
console.log(foo.get("bar")); // banana!
console.log(bar.get("bar")); // undefined.
bar.set("bar", "apple");
console.info(foo.get("bar"), bar.get("bar")); // banana apple

edit why that is...

my reliance on mootools means my understanding of native js prototypes leaves something to be desired as it abstracts you having to deal with this directly but..

in pattern one, you define AND run the function, which creates the prototype and sets data - a singular instance. you then create new functions with that 'live' prototype where data is already set.

in pattern two, a brand new prototype is created and referenced for each instance, independent of each other. your function returns a new Function with prototype Class... so really new Class({}); hence new new <var>() will create and instantiate the class.

to understand this better, perhaps you can write it like this first - a common enough pattern for creating and instantiating a class that is not being reused - which will make more sense:

new (new Class({
    initialize: function() {
        alert("hi");
    }
}))();

which in turn can be written like this (if saved into a variable):

var foo = new Class({
    initialize: function() {
        alert("hi");
    }
});

new foo();

I hope it makes sense, I am not the best person at explaining...

like image 80
Dimitar Christoff Avatar answered Oct 16 '22 14:10

Dimitar Christoff