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

Discussion (18)

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 Author

this looks very helpful

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 Author

a good one 👍

Collapse
lukeshiru profile image
Luke Shiru

You don't actually need a Proxy to have private properties in JavaScript, but is ok to use it for stuff like tracking changes (maybe for some kind of analytics). For the private properties You could use a class:

class User {
    #sex;

    constructor({ name, age, sex }) {
        this.name = name;
        this.age = age;
        this.#sex = sex;
    }
}

const logger = object =>
    new Proxy(object, {
        get(target, property) {
            console.log(`Reading the property ${property}`);
            return target[property];
        },
    });

const rajesh = logger(
    new User({
        name: "Rajesh Royal",
        age: 23,
        sex: "Male",
    }),
);

rajesh.age; // Logs: "Reading the property age"
rajesh.#sex; // Uncaught SyntaxError: Private field '#sex' must be declared in an enclosing class
Enter fullscreen mode Exit fullscreen mode

Or better yet, just use closures:

const createUser = ({ name, age, sex }) => ({
    name,
    age,
});

const logger = object =>
    new Proxy(object, {
        get(target, property) {
            console.log(
                target[property] !== undefined
                    ? `Reading the property ${property}`
                    : `Reading missing property ${property}`,
            );
            return target[property];
        },
    });

const rajesh = logger(
    createUser({
        name: "Rajesh Royal",
        age: 23,
        sex: "Male",
    }),
);

rajesh.age; // Logs: "Reading the property age"
rajesh.sex; // Logs: "Reading missing property sex"
Enter fullscreen mode Exit fullscreen mode

One good use case for Proxy from my point of view is simple runtime immutability:

const immutable = object =>
    new Proxy(object, {
        get: (target, property) =>
            typeof target[property] === "object"
                ? immutable(target[property])
                : target[property],
        set: () => true,
    });

const example = immutable({ name: "Luke Shiru" });

console.log(example.name); // Logs "Luke Shiru"
example.name = "Nope"; // Nothing changes 
console.log(example.name); // Still logs "Luke Shiru"
Enter fullscreen mode Exit fullscreen mode

Hope that helps a little.

Cheers!

Collapse
rajeshroyal profile image
Rajesh Royal Author

man these are really very helpful 🤘 thanks

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 Author

really very good example 👌

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
ivanjeremic 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 Author

🙃🙃

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 Author

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 Author

Thanks man.