DEV Community

Rajesh Royal
Rajesh Royal

Posted on

What is the use of Proxy and Reflect in JavaScript?

I have read the documentation for Proxy and Reflect on MDN but didn't understand much. Can anyone please explain or mention the real world use of these ES6 features.

  • One of the good use of proxy I found is that we can declare our object values as private -
const object = {
    name: "Rajesh Royal",
    Age: 23,
    _Sex: "Male"
}

const logger = {
    get(target, property){
        if(property.startsWith("_")){
            throw new Error(`${property} is a private property.`);
        }

        console.log(`Reading the property ${property}`);
        return target[property];
    }
}

const Logger = new Proxy(object, logger);

// now if you try to access the private property it will throw an error
Logger._Sex

Uncaught Error: _Sex is a private property.
    at Object.get (<anonymous>:4:19)
    at <anonymous>:1:8
Enter fullscreen mode Exit fullscreen mode

Top comments (17)

Collapse
 
shuckster profile image
Conan

I posted this elsewhere, but here's an example use-case:

Let's say you want to use arrays as accessors, like this:

const array = "the five boxing wizards jump quickly".split(" ");
const words = fancy(array);

words[[3, -1, -2]];
// ["wizards", "quickly", "jump"]

words[-1] = "swiftly";
// ["the", "five", "boxing", "wizards", "jump", "swiftly"]

words[[0, -1]] = ["alpha", "omega"];
// ["alpha", "five", "boxing", "wizards", "jump", "omega"]
Enter fullscreen mode Exit fullscreen mode

You can use Proxy and Reflect to achieve it:

const parseIndexesString = (indexes, maxIndex) =>
  indexes
    .split(",")
    .map((x) => x.trim())
    .map((x) => (/^-\d+$/.test(x) ? maxIndex + +x : x));

const fancyArrayAccessors = {
  get(obj, prop) {
    if (/^\d+$/.test(prop)) {
      return Reflect.get(...arguments);
    }
    const props = parseIndexesString(prop, obj.length);
    return props.map((prop) => obj[prop]);
  },
  set(obj, prop, value) {
    if (/^\d+$/.test(prop)) {
      return Reflect.get(...arguments);
    }
    const props = parseIndexesString(prop, obj.length);
    return props.map((prop, idx) => 
      (obj[prop] = Array.isArray(value) ? value[idx] : value)
    );
  }
};

const fancy = (arr) => new Proxy(arr, fancyArrayAccessors);
Enter fullscreen mode Exit fullscreen mode

Here, Reflect is being used to take care of the "default" case of accessing an array using a normal numerical index.

The rest of the time we're abusing the fact that any key in arr[key] form gets coerced into a string, so can be parsed into whatever we like once it reaches the getter/setter in a Proxy handler.

Collapse
 
rajeshroyal profile image
Rajesh Royal

this looks very helpful

Collapse
 
sainig profile image
Gaurav Saini

I’m not too familiar with Reflect, but have a pretty good analogy for proxies that I’m sure will be helpful
If you know about Express and how the whole middleware pattern works, then proxies are exactly like middleware but for plain JavaScript objects. So, whenever you read from and object or make any changes to an object you can execute custom behaviour on those operations.
And as for the use cases, the only real ones I can think of is when you’re building some framework.

Collapse
 
rajeshroyal profile image
Rajesh Royal

really very good example 👌

Collapse
 
peerreynders profile image
peerreynders

First time I came across Reflect was in the web components native shim:

  const wrapperForTheName = {
    'HTMLElement': function HTMLElement(this: HTMLElement) {
      return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
    },
  };
  window.HTMLElement = (wrapperForTheName[
    'HTMLElement'
  ] as unknown) as typeof HTMLElement;
  HTMLElement.prototype = BuiltInHTMLElement.prototype;
  HTMLElement.prototype.constructor = HTMLElement;
  Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
Enter fullscreen mode Exit fullscreen mode

MDN: Reflect.construct().

Browsers implement custom elements as native classes (separate from JS classes).

Some tooling insists on compiling down to ES5 and using Reflect.construct() instead of new makes it possible to use custom elements from ES5 code (obviously the browser has to support custom elements which implies that it also supports classes (ES2015)).


Collapse
 
rajeshroyal profile image
Rajesh Royal

a good one 👍

Collapse
 
jankapunkt profile image
Jan Küster

I wrote a tiny package that uses proxy to create factories for any of your existing es6 (or old es5) classes and allows to define private fields or function based on a rule function: github.com/jankapunkt/js-class-pri...

No need to fully rewrite the classes to support private members (plus I still dislike the choice of the hash character for the private fields feature)

Collapse
 
ninjainpajama profile image
ninja in pajama

well, hard to imagine a use case for that unless you're building your own framework or a library. If someone asks me such stuff in an interview, I would go 'cmon are you doing some rocket science'?)

Collapse
 
ivan_jrmc profile image
Ivan Jeremic

Right, I for example use proxies for reactivity in the front-end framework I build.

Collapse
 
codingjlu profile image
codingjlu

But there's this point I force myself to learn anything and everything about JavaScript just to convince myself I'm good at it :/.

Collapse
 
rajeshroyal profile image
Rajesh Royal

🙃🙃

Collapse
 
rodrigocnascimento profile image
Rodrigo Nascimento

Once I used Reflect on a jest test to simulate the class/ method spyOn

Collapse
 
rajeshroyal profile image
Rajesh Royal

Can you please share code snippet if possible.

Collapse
 
rodrigocnascimento profile image
Rodrigo Nascimento
repositoryClass = module.get<IRepo>(repo);
const queryBuilder = jest.fn().mockReturnValue({
    getMany: jest.fn().mockReturnValue({ ...object }),
});
Reflect.set(
    repositoryClass,
    'queryBuilderMethod',
    queryBuilder
);
Enter fullscreen mode Exit fullscreen mode

I was using typeorm + nestjs + jest so it has a queryBuilder and I wanted that my custom queryBuilderMethod "inherits" some default queryBuilder methods, because in that case, I was using the default plus the custom.
So my other custom methods that depends on those configuration custom + default will have the right params and return the query as intended

Collapse
 
ninjainpajama profile image
ninja in pajama

You may find this helpfull - use-es6-proxies-to-enhance-your-ob...

Collapse
 
rajeshroyal profile image
Rajesh Royal

Thanks man.

Collapse
 
rajeshroyal profile image
Rajesh Royal

man these are really very helpful 🤘 thanks