Skip to content

Instantly share code, notes, and snippets.

@blogui91
Forked from ifraixedes/LICENSE
Created March 5, 2018 02:24
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 blogui91/fee19c9d2f71a6c81a3d3b4b026b2617 to your computer and use it in GitHub Desktop.
Save blogui91/fee19c9d2f71a6c81a3d3b4b026b2617 to your computer and use it in GitHub Desktop.
Private properties on ES6 Classes using Proxies
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004
(http://www.wtfpl.net/about/)
Copyright (C) 2015 Ivan Fraixedes (https://ivan.fraixed.es)
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "private-prop-es6-class-with-proxy",
"version": "0.0.0",
"description": "Private properties on ES6 Classes using Proxies",
"main": "private-props-es6-class-with-proxy.js",
"dependencies": {
"harmony-reflect": "^1.1.3"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Ivan Fraixedes <ivan@fraixed.es> (http://ivan.fraixed.es)",
"license": "WTFPL"
}
#! iojs --harmony --harmony_proxies
'use strict';
require('harmony-reflect');
// Usual class
class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(name) {
this._name = name;
}
greeting(person) {
return `hi ${person.name}`;
}
}
// Create an object from an usual class
let ivanPerson = new Person('Ivan');
// Call the getter of _name
console.log(`name getter from a Person instance: ${ivanPerson.name}`);
// _name property is accessible because JS classes doesn't have access scope
console.log(`_name property from a Person instance: ${ivanPerson._name}`);
// Let's try to protect class object
let ProtectedPerson = new Proxy(Person, {
get(target, name) {
console.log('calling getter in Person wrapped by a proxy');
if (name.startsWith('_')) {
throw new Error('Accessing to a private property is not allowed');
} else {
return target[name];
}
}
});
// Let's create an instance of the class that we wrapped with a proxy
let ivanProtectedPerson = new ProtectedPerson('Ivan');
// Call the getter of _name
console.log(`name getter from a protected person instance: ${ivanProtectedPerson.name}`);
// _name still accessible due proxy wrapped class Person;
// a class is a Function with prototype object, proxy trap calls to the
// class itself hence the target is the class, not its instances
console.log(`_name property from a protected person instance: ${ivanProtectedPerson._name}`);
// let's protect a base class instance
let ivanPersonProtected = new Proxy(ivanPerson, {
get(target, name) {
if (name.startsWith('_')) {
throw new Error('Accessing to a private property is not allowed');
} else {
return target[name];
}
}
})
// Call the getter of _name
console.log(`name getter from a person instance which has been protected: ${ivanPersonProtected.name}`);
try {
// _name property call gets trapped by the proxy
console.log(`_name property from a person instance which has been protected: ${ivanPersonProtected._name}`);
} catch (e) {
if (e.message === 'Accessing to a private property is not allowed') {
console.log('Proxy did its job!!');
} else {
throw e;
}
}
// However it's painful, having to wrap in a proxy every single instance of a class
// is a repeatable tiring task, so let's try to creata a class that protect its
// object instances
class PersonProtected {
constructor(name) {
this._name = name;
// In es6 it also works as in es5: remember es6 class is nothing more than a Function
// and `new` call the function defined by `constructor`;
// in es5 works because when you call a `new` on a function the value retuned is the
// value returned inside the function if it's an object otherwise returns `this`;
// in es6 remains the same for backward compatibility
return new Proxy(this, {
get(target, name) {
if (name.startsWith('_')) {
throw new Error('Accessing to a private property is not allowed');
} else {
return target[name];
}
}
});
}
get name() {
return this._name;
}
set name(name) {
this._name = name;
}
greeting(person) {
return `hi ${person.name}`;
}
}
let ivanPersonProtectedInstance = new PersonProtected('Ivan');
// Call the getter of _name
console.log(`name getter from a PersonProtected instance: ${ivanPersonProtectedInstance.name}`);
try {
// _name property call gets trapped by the proxy
console.log(`_name property from a PersonProtected instance: ${ivanPersonProtectedInstance._name}`);
} catch (e) {
if (e.message === 'Accessing to a private property is not allowed') {
console.log('Class which proxy `this` did its job!!');
} else {
throw e;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment