From ES6, there are Proxy and Reflect classes which allow us to customize the objects at several language levels. For example: property retrieval, assignment, iteration, function invocation, ...

Lets start with several samples.

Example 1: Programmable property retrieval

Lets see an example that you want an object whose properties' values are the properties' names. I.e., obj.a = 'a', obj.b = 'b', ...
With Proxy and Reflect, we can have the following implementation to accomplish this.

const obj = new Proxy({}, {get(o, propName){return propName}})
obj.a // = 'a'
obj.b // = 'b'
obj.c // = 'c'

Example 2: customize delete operator.

Requirement: print to console when a property is removed.

const obj = new Proxy({}, {deleteProperty(o, propName){console.log(`${propName} is removed`)}})
delete obj.a // print 'a is removed'
delete obj.a // print 'b is removed'

And many other handlers are supported with their associated Reflect's methods, namely:

To reproduce the original behavior of the object in the trap handler, call the associated Reflect's method with the same passed in parameters. For example,

new Proxy(obj, {deleteProperty(...params){return Reflex.deleteProperty(...params)}})

There is also Proxy.revocable(), used to enable the proxy to switch off (at most once).