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:

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…


1 I assume that in this case you can’t use an IIFE because other parts of your library need to access these properties.

Everything on my blog is under the CC BY 4.0 unless stated otherwise.