Last active
August 30, 2016 08:03
-
-
Save kofifus/2379c595c8fb5630f7927b639d51d157 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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