Skip to content

Instantly share code, notes, and snippets.

@mnjul
Last active February 4, 2018 19:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mnjul/21046553880306ec13603656ccda2b7e to your computer and use it in GitHub Desktop.
Save mnjul/21046553880306ec13603656ccda2b7e to your computer and use it in GitHub Desktop.
// Apache License: https://www.apache.org/licenses/LICENSE-2.0
;(function (exports){
// class -> class private methods name Set()
let classPrivateMethodsMap = new WeakMap();
// classPrivateMethods -> WeakMap (instance -> members)
let privateMemberMap = new WeakMap();
function createInternalFunction(classType,
classPrivateMethods,
classPrivatePropertyNames) {
// guard against outsider mangling
if (classPrivateMethodsMap.has(classType)) {
throw new Error(`Class ${classType.name} already encountered in createInternalFunction`);
}
let superClassType = Object.getPrototypeOf(classType);
while (superClassType !== Function.prototype) {
let superClassPrivateMethodNames = classPrivateMethodsMap.get(superClassType);
if (superClassPrivateMethodNames) {
[
...Object.keys(classPrivateMethods),
...Object.getOwnPropertyNames(classType.prototype)
].forEach(methodName =>
{
if (superClassPrivateMethodNames.has(methodName)) {
throw new Error(`Class ${classType.name} defines a method ` +
`named ${methodName} but base class ${superClassType.name} ` +
'already defined it as private method');
}
});
}
superClassType = Object.getPrototypeOf(superClassType);
}
classPrivateMethodsMap.set(
classType,
new Set(Object.keys(classPrivateMethods))
);
if (!privateMemberMap.has(classPrivateMethods)) {
privateMemberMap.set(classPrivateMethods, new WeakMap());
}
function generatePrivateMemberObject(instance) {
let obj = {};
Object.entries(classPrivateMethods)
.forEach(([name, func]) => {
obj[name] = func.bind(instance);
});
return obj;
}
return function(instance) {
if (!privateMemberMap.get(classPrivateMethods).has(instance)) {
privateMemberMap.get(classPrivateMethods).set(
instance,
generatePrivateMemberObject(instance)
);
}
return privateMemberMap.get(classPrivateMethods).get(instance);
}
}
exports.createInternalFunction = createInternalFunction;
})(window);
;(function (exports){
const privateMethods = Object.seal({
_privMethod1() {
console.log(this.constructor.name, "At base private method 1 on obj", _(this)._privProperty);
},
});
const privatePropertyNames = Object.seal(new Set([
'_privProperty'
]));
class BaseClass {
constructor(property) {
_(this)._privProperty = property;
}
pubMethod1() {
console.log(this.constructor.name, "At base public method 1 on obj", _(this)._privProperty);
_(this)._privMethod1();
}
}
let _ = createInternalFunction(BaseClass, privateMethods, privatePropertyNames);
exports.BaseClass = BaseClass;
})(window);
;(function (exports){
// You may need to comment out re-definitions on private members to make this
// class work, since current code demonstrates how re-definitions on private
// members are disallowed.
const privateMethods = Object.seal({
_privMethod1() {
console.log(this.constructor.name, "At derived private method 1 on obj", _(this)._privProperty);
},
});
const privatePropertyNames = Object.seal(new Set([
'_privProperty'
]));
class DerivedClass extends BaseClass {
constructor(property) { // Note: |property| unused here intentionally
super('base-property');
console.log(this.constructor.name, "_(this)._privProperty = ", _(this)._privProperty);
}
_privMethod1() {
}
pubMethod3(property) {
_(this)._privProperty = property;
console.log(this.constructor.name, "_(this)._privProperty = ", _(this)._privProperty);
this.pubMethod1();
}
}
let _ = createInternalFunction(DerivedClass, privateMethods, privatePropertyNames);
exports.DerivedClass = DerivedClass;
})(window);
let obj = new DerivedClass('derived-property');
console.log('--');
obj.pubMethod3('new-derived-property');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment