Skip to content

Instantly share code, notes, and snippets.

@DavidBruant
Created April 1, 2012 21:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DavidBruant/2279059 to your computer and use it in GitHub Desktop.
Save DavidBruant/2279059 to your computer and use it in GitHub Desktop.
Protected protocol
var d = new Person('David');
console.log('d.name:', d.name);
setTimeout(function(){
console.log('d age', d.getAge());
}, 500);
console.log('d instanceof Person', d instanceof Person);
console.log('d instanceof ComputerSavvyPerson', d instanceof ComputerSavvyPerson);
var db = new ComputerSavvyPerson('David Bruant', 'JavaScript');
console.log('db.name:', db.name);
console.log('db.favoriteLanguage:', db.favoriteLanguage);
console.log('first letter secret', db.getSecretFirstLetter());
setTimeout(function(){
console.log('db age', db.getAge());
}, 500);
console.log('db instanceof Person', db instanceof Person);
console.log('db instanceof ComputerSavvyPerson', db instanceof ComputerSavvyPerson);
var eve = new EvilPerson();
eve.attemptStealSecret(d);
eve.attemptStealSecret(db);
var eve2 = new EvilCompSavvyPerson();
eve2.attemptStealSecret(d);
eve2.attemptStealSecret(db);
// COMPUTER SAVVY PERSON
var ComputerSavvyPerson = makeSubclass(Person, function(Protected, Super){
function ComputerSavvyPerson(name, favoriteLanguage){
console.log('this in ComputerSavvyPerson', this);
Super.call(this, name);
this.favoriteLanguage = favoriteLanguage;
}
ComputerSavvyPerson.prototype = Super.protoChain({
getSecretFirstLetter : function(){
return Protected(this).secret[0];
}
});
//console.log('ComputerSavvyPerson.prototype', ComputerSavvyPerson.prototype)
return ComputerSavvyPerson;
});
function evilPersonDecl(Protected, Super){
function EvilPerson(){
this.attemptStealSecret = function(otherPerson){
//console.log('Evil Person', 'Protected(otherPerson).secret read')
var s = Protected(otherPerson).secret;
console.log("Ahah! I tried to steal %s's secret! Here is what I got:%s", otherPerson.name, String(s));
}
}
return EvilPerson;
}
var EvilPerson = makeSubclass(Person, evilPersonDecl);
var EvilCompSavvyPerson = makeSubclass(ComputerSavvyPerson, evilPersonDecl);
// PERSON
var Person = makeConstructorWithProtected(function(Protected){
function Person(name){
Protected(this).secret = String(Math.random()).substr(2);
console.info("person's secret", Protected(this).secret, "...shhhhhhh!!!...");
Protected(this).age = 0;
setInterval(function(){
Protected(this).age++;
}.bind(this), 100);
this.name = name;
console.log('this in Person', this);
}
Person.prototype = {
getAge: function(){
return Protected(this).age;
}
};
return Person;
});
function makeProtectedStore(){
var ownStore = new WeakMap();
var childStoreGets = [];
var ret = function(o){
var protectedStore;
// own store first
protectedStore = ownStore.get(o);
if(protectedStore)
return protectedStore;
// check in otherStores
childStoreGets.some(function(storeGet, i){
//console.log('traversing childStores', childStores.length, i);
var s = storeGet(o);
if(s === Object(s)){
protectedStore = s;
return true;
}
return false;
});
if(protectedStore){
return protectedStore;
}
else{
var s = {}; // create new store
ownStore.set(o, s);
return s;
}
};
ret.addChildStoreGet = childStoreGets.push.bind(childStoreGets);
ret.storeGet = ownStore.get.bind(ownStore);
return Object.freeze(ret);
}
(function(global){
var constructorToProtectedStore = new WeakMap();
global.makeConstructorWithProtected = function(declaration){
var protectedStore = makeProtectedStore();
var constructorWithProtected = declaration(protectedStore);
constructorToProtectedStore.set(constructorWithProtected, protectedStore);
return constructorWithProtected;
}
global.makeSubclass = function(parent, declaration){
var protectedStore = makeProtectedStore();
var parentProtectedStore = constructorToProtectedStore.get(parent);
parentProtectedStore.addChildStoreGet(protectedStore.storeGet);
var constructorWithProtected = declaration(protectedStore, Object.freeze({
call: function(obj){
var args = Array.prototype.slice.call(arguments, 1);
protectedStore(obj); // so that there is already an own protected store for the subclass instance
return parent.apply(obj, args);
},
protoChain : function(obj){
var proto = Object.create(parent.prototype);
Object.getOwnPropertyNames(obj).forEach(function(n){
Object.defineProperty(proto, n, Object.getOwnPropertyDescriptor(obj, n));
});
return proto;
},
prototype: parent.prototype
}));
constructorToProtectedStore.set(constructorWithProtected, protectedStore);
return constructorWithProtected;
}
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment