Last active
August 14, 2019 15:49
-
-
Save XoseLluis/b6a3d96f22b48b32d961b83b1771f3e4 to your computer and use it in GitHub Desktop.
Privatize a JavaScript (TypeScript) method at runtime
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
function calledFromInsideClass(className:string):boolean{ | |
let stackTrace = (new Error()).stack; // Only tested in latest FF and Chrome | |
//console.log("stackTrace: " + stackTrace); | |
stackTrace = (stackTrace as string).replace(/^Error\s+/, ''); // Sanitize Chrome | |
let callerLine = stackTrace.split("\n")[2]; // 1st item is this function, 2nd item is the privatizedMethod, 3rd item is the caller | |
return callerLine.includes(className + "."); | |
} | |
function privatizeMethod(classFn: any, methodName: string){ | |
let originalMethod = classFn.prototype[methodName]; | |
let privatizedMethod = function(){ | |
if (calledFromInsideClass(classFn.name)){ | |
console.log("privatized method called from inside"); | |
return originalMethod.call(this, ...arguments); | |
} | |
else{ | |
throw new Error(`privatized method ${originalMethod.name} called`); | |
} | |
}; | |
classFn.prototype[methodName] = privatizedMethod; | |
} | |
class City{ | |
constructor(private name: string, | |
private extension: number, | |
private population: number){} | |
public growCity(percentage: number){ | |
console.log("growCity invoked"); | |
this.growExtension(percentage); | |
this.growPopulation(percentage); | |
} | |
public growExtension(percentage: number){ | |
console.log("growExtension invoked"); | |
this.extension = this.extension + (this.extension * percentage /100); | |
} | |
public growPopulation(percentage: number){ | |
console.log("growPopulation invoked"); | |
this.population = this.population + (this.population * percentage /100); | |
} | |
private privateMethod(){ | |
console.log("I'm a private method"); | |
} | |
} | |
let city = new City("Paris", 105, 2140000); | |
//city.privateMethod(); | |
//trick to invoke a private method | |
(city as any).privateMethod(); | |
city.growCity(2); | |
console.log(JSON.stringify(city, null, "\t")); | |
//privatize the the City.growPopulation method | |
privatizeMethod(City, City.prototype.growPopulation.name); | |
city.growCity(3); //continues to work fine, it invokes internally our new private method | |
try{ | |
city.growPopulation(3); | |
} | |
catch(ex){ | |
console.log("EXCEPTION: " + ex.message); ////EXCEPTION: privatized method growPopulation called | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment