Hiding properties in JS... for real
Sometimes, you want to hide the property of an object1. Probably you are a library author and you are afraid that:
- your users will see it and attempt to use it
- your users will try to enumerate over it although it might not make sense
So here is what you do:
const protectedObj = Object.defineProperty({}, 'foo', {
enumerable: false,
configurable: false,
writable: false,
value: 'hi there'
});
console.log(protectedObj.foo); // 'hi there'
console.log(Object.keys(protectedObj)); // []
Mission accomplished!
Well, nope, of course not.
JavaScript provides your users with Object.getOwnPropertyNames() or Reflect.ownKeys(), which will reveal the property nevertheless:
console.log(Object.getOwnPropertyNames(protectedObj)); // [ 'foo' ]
console.log(Reflect.ownKeys(protectedObj)); // [ 'foo' ]
You should probably leave it at that, because if your users are using these, they know what to expect.
But anyway, here is a funny trick that puzzled me for a moment a few days ago.
Proxy
to the rescue
The trick to really hide properties is to use Proxy. The idea is that you set a trap, a function that is executed when the user tries to run Object.getOwnPropertyNames()
or Reflect.ownKeys()
, instead of whatever the JavaScript engine is doing internally. And you decide what the function returns - or in this case, doesn’t.
const hardProtectedObj = new Proxy({
foo: 'hi there',
}, {
ownKeys: (target) => {
return [];
},
});
console.log(hardProtectedObj.foo); // 'hi there'
console.log(Object.keys(hardProtectedObj)); // []
console.log(Object.getOwnPropertyNames(hardProtectedObj)); // []
console.log(Reflect.ownKeys(hardProtectedObj)); // []
I believe this is the ultimate solution, until JavaScript gets friend classes. On the other hand…
- I find it needlessly complicated
- my gut feeling is that it goes against the freedom that JavaScript gives you - for better or worse