Proxy in JS

Kartikey KatyalKartikey Katyal
3 min read

What is a Proxy in JavaScript?

A Proxy object wraps another object and allows us to control its behaviour. We can intercept fundamental operations like property access get, property assignment set, and even method calls.

Think of it as a gatekeeper that lets you define custom rules for interacting with an object.

Example: Enabling Negative Indexing for Arrays

Let’s say we want to access arr[-index] to get the last element of an array, arr[-2] for the second last, and so on. JavaScript doesn’t allow this by default, but we can override the behaviour using a Proxy.

jsCopyEditconst arr = [1, 2, 3, 4, 5, 6];

function createNegativeIndexProxy(array) {
    return new Proxy(array, {
        get(target, prop) {
            const index = Number(prop);
            if (!isNaN(index)) {
                return index < 0 ? target[target.length + index] : target[index];
            }
            return target[prop]; // Handle non-numeric properties normally
        },
        set(target, prop, value) {
            const index = Number(prop);
            if (!isNaN(index)) {
                if (index < 0) {
                    target[target.length + index] = value;
                } else {
                    target[index] = value;
                }
                return true;
            }
            return false;
        }
    });
}

let negArr = createNegativeIndexProxy(arr);

console.log(negArr[-1]); // Output: 6
console.log(negArr[-2]); // Output: 5
console.log(negArr[2]);  // Output: 3

negArr[-1] = 10;  
console.log(arr); // [1, 2, 3, 4, 5, 10]

Breaking it Down

  1. Intercepting property access (get)

    • When negarr[-1] is accessed, we convert -1 into arr.length - 1, effectively retrieving arr[arr.length-1]

    • If the property is not a number (e.g., negArr.length), we return the normal value.

  2. Intercepting property assignment (set)

    • If a negative index is used, it modifies the corresponding element from the end.

    • Otherwise, it works like normal.

Why Use Proxies?

  • Flexibility: We can redefine how objects behave dynamically.

  • Custom Features: Adds functionality like negative indexing without modifying the original Array.prototype.

  • Encapsulation: Keeps the logic contained within the proxy, avoiding side effects.

Besides get and set, JavaScript Proxy supports many other trap methods that allow you to customize an object's behavior. Here are some of the most useful ones:

TrapDescription

get(target, prop)

Intercepts property access (obj.prop)

set(target, prop, value)

Intercepts property assignment (obj.prop = value)

apply(target, thisArg, argsList)

Intercepts function calls

construct(target, args, newTarget)

Intercepts new keyword

has(target, prop)

Intercepts prop in obj

deleteProperty(target, prop)

Intercepts delete obj.prop

ownKeys(target)

Intercepts Object.keys() or for...in

defineProperty(target, prop, descriptor)

Intercepts Object.defineProperty()

getOwnPropertyDescriptor(target, prop)

Intercepts Object.getOwnPropertyDescriptor()


const docCookies = new Proxy(docCookies, {
  get(target, key) {
    return target[key] ?? target.getItem(key) ?? undefined;
  },
  set(target, key, value) {
    if (key in target) {
      return false;
    }
    return target.setItem(key, value);
  },
  deleteProperty(target, key) {
    if (!(key in target)) {
      return false;
    }
    return target.removeItem(key);
  },
  ownKeys(target) {
    return target.keys();
  },
  has(target, key) {
    return key in target || target.hasItem(key);
  },
  defineProperty(target, key, descriptor) {
    if (descriptor && "value" in descriptor) {
      target.setItem(key, descriptor.value);
    }
    return target;
  },
  getOwnPropertyDescriptor(target, key) {
    const value = target.getItem(key);
    return value
      ? {
          value,
          writable: true,
          enumerable: true,
          configurable: false,
        }
      : undefined;
  },
});

/* Cookies test */

console.log((docCookies.myCookie1 = "First value"));
console.log(docCookies.getItem("myCookie1"));

docCookies.setItem("myCookie1", "Changed value");
console.log(docCookies.myCookie1);
3
Subscribe to my newsletter

Read articles from Kartikey Katyal directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kartikey Katyal
Kartikey Katyal