Last active
February 4, 2018 19:43
-
-
Save mnjul/21046553880306ec13603656ccda2b7e to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// 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