Skip to content

Instantly share code, notes, and snippets.

@kofifus
Last active August 30, 2016 08:03
Show Gist options
  • Save kofifus/2379c595c8fb5630f7927b639d51d157 to your computer and use it in GitHub Desktop.
Save kofifus/2379c595c8fb5630f7927b639d51d157 to your computer and use it in GitHub Desktop.
Going over the usually recommended 'classes' solutions I ended up dissatisfied with all of them, mainly:
- using ES6 class, I have no real private variables/methods and my code is full with this._ which is really ugly
- using the crockford way, private methods have this unaassigned (or ===window) which is very confusing and
error prone and need the 'that=this' hack thing to work properly
- other soloutions ie weakmaps and symbols create complicated ugly code
- in any of the above ways it is hard to have a clear view of the public interface of the class
Below is a what I feel is a better solution allowing:
- no need for 'this._', that/self, weakmaps, symbols etc
- 'this.' is only needed for a private method accessing a public method. In fact if the public interface is just a
proxy to private methods, 'this' is not needed at all.
- private variables and methods are _really_ private and have the correct 'this' binding
- public interface is clear and separated from the implementation
As the code is ATM there is no support for inheritance, but I'm sure this can be added quite easily. Personally I am
trying to avoid inheritance in favour of composition.
// create a new object with this function as construcor
Function.prototype.New = function(...args) {
let ins=Object.create(null); // the new instance
let pub=this.apply(ins, args); // returned public interface
// shallow clone public interface (including getters/setters) into instance
// this allows the private methods to access the public methods
// an alternative approach can be to probhibit private methods from accessing public methods (so that the
// public interface is just a proxy), in which case delete the block below
var props = Object.getOwnPropertyNames(pub);
props.forEach(function(key) {
var desc = Object.getOwnPropertyDescriptor(pub, key);
Object.defineProperty(ins, key, desc);
});
return pub;
}
// A 'class'
function F(defval) {
// private interface, 'this' here is the instance
// use this. to access the public interface
// private variables
let pval = (defval || 0);
// private methods - use ONLY arrow functions for correct 'this' binding
let prv1 = () => {
console.log('prv1 '+pval); // private method calling private variable
prv2(); // private method calling private method
}
let prv2 = () => {
console.log('prv2 '+this.pub3()+' '+this.val); // private method calling public method (& public getter)
};
// public interface
return {
pub1() { return prv1(); }, // public method calling private method
pub2() { return this.pub1(); }, // public method calling public method
pub3() { return 'pub3'; },
get val() { return pval; },
set val(v) { pval = v; }
}
}
// usage
let o1 = F.New(5);
o1.val = 100;
o1.pub1();
let o2 = F.New(7);
o2.pub2();
//p2.prv1(); // error - prv1 is private
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment