Reputation: 27204
I'm trying to create a function that hides private properties on Objects as well as possible. I would define private properties here as those that begin with an underscore, eg. _password
.
Below is what I've got so far (thanks to Nicolas Bevacqua's great intro to proxies).
Now I was wondering:
Reflect
methods in conjunction with a proxy? And do I even need them here?My function so far:
function privatize(obj, prefix = '_', throwError = false) {
const proxyHandler = {
get(target, key) {
return private(key, 'get') ? undefined : Reflect.get(target, key);
},
set(target, key, value) {
return private(key, 'set') ? undefined : Reflect.set(target, key, value);
},
has(target, key) {
return private(key, 'has') ? false : Reflect.has(target, key);
},
deleteProperty(target, key) {
return private(key, 'delete') ? false : Reflect.deleteProperty(target, key);
},
defineProperty(target, key, descriptor) {
return private(key, 'defineProperty') ? false : Reflect.defineProperty(target, key, descriptor);
},
enumerate(target) {
return Object.keys().filter((key) => {
return !private(key, null, false);
})[Symbol.iterator]();
},
ownKeys(target) {
return Reflect.ownKeys(target).filter((key) => {
return !private(key, null, false);
});
},
getOwnPropertyDescriptor(target, key) {
return private(key, 'getOwnPropertyDescriptor') ? false : Reflect.getOwnPropertyDescriptor(target, key);
}
};
function private(key, operationName) {
if (key.indexOf(prefix) === 0) {
if (throwError) {
throw new Error(`Operation '${operationName}' is not allowed on private properties.`);
}
return true;
}
}
return new Proxy(obj, proxyHandler);
}
var o = {
first: 'should work',
_second: 'should fail'
};
var proxied = privatize(o);
console.log(proxied);
PS: For native browser support, you might have to look at it in MS Edge or Firefox Dev Edition.
http://jsfiddle.net/bkd7mde7/1/
Upvotes: 4
Views: 1890
Reputation:
You need to be aware of the concept of "invariants". For instance, if an object is non-extensible, then it is not allowed to hide its properties via the proxy, and a non-configurable property may not be hidden. You cannot defineProperty
a property it does not already have. getOwnPropertyDescriptor
must return an object or undefined
. deleteProperty
cannot delete a non-configurable property. set
cannot change a non-writable, non-configurable property. Any or all of these could cause your code to fail (by throwing at run-time) in various scenarios.
Other minor issues include the fact that set
is supposed to return a boolean (success/failure), although I'm not sure what would happen with the undefined
that your are returning.
Upvotes: 3
Reputation: 92531
There are a couple of issues with your code:
throwError = throwError
in the private
function's parameters is superfluous (and in fact doesn't even work, at least in the latest Chrome), because throwError
will be available inside the function anyway.getOwnPropertyDescriptor
trap should never return false
. It should return undefined
for non-existing properties.enumerate
trap is obsolete.Reflect.preventExtensions()
on the proxied
object breaks it. You should prevent preventing extensions of your object by adding a preventExtensions
trap and returning false
inside it.Upvotes: 2