JavaScript Easy technical 1 views 2 min read

What are Proxy traps and what operations can they intercept?

Peer-reviewed by HireXTech Technical Panel Updated for 2025/2026 hiring Editorial standards
Practise this track
Interviewer Expectations for this Question
01
Core Competency

Assesses fundamental understanding of JavaScript conventions, runtime behavior, and memory/performance considerations.

02
Evaluation Criteria

Hiring managers look for precision, avoidance of ambiguous jargon, and ability to explain trade-offs under real production conditions.

Comprehensive Model Answer Verified Solution

Proxy traps are handler methods that intercept fundamental operations on objects, allowing you to customize their behavior.

Available traps:

     const handler = {
       // Property access
       get(target, prop, receiver) {
         console.log(`Getting ${prop}`);
         return Reflect.get(target, prop, receiver);
       },

       // Property assignment
       set(target, prop, value, receiver) {
         console.log(`Setting ${prop} to ${value}`);
         return Reflect.set(target, prop, value, receiver);
       },

       // Property deletion
       deleteProperty(target, prop) {
         console.log(`Deleting ${prop}`);
         return Reflect.deleteProperty(target, prop);
       },

       // 'in' operator
       has(target, prop) {
         console.log(`Checking ${prop}`);
         return Reflect.has(target, prop);
       },

       // Object.keys, for...in
       ownKeys(target) {
         return Reflect.ownKeys(target);
       },

       // Function calls
       apply(target, thisArg, args) {
         console.log(`Called with ${args}`);
         return Reflect.apply(target, thisArg, args);
       },

       // new operator
       construct(target, args) {
         console.log(`Constructed with ${args}`);
         return Reflect.construct(target, args);
       },

       // Object.getPrototypeOf
       getPrototypeOf(target) {
         return Reflect.getPrototypeOf(target);
       },

       // Object.setPrototypeOf
       setPrototypeOf(target, proto) {
         return Reflect.setPrototypeOf(target, proto);
       },

       // Object.isExtensible
       isExtensible(target) {
         return Reflect.isExtensible(target);
       },

       // Object.preventExtensions
       preventExtensions(target) {
         return Reflect.preventExtensions(target);
       },

       // Object.getOwnPropertyDescriptor
       getOwnPropertyDescriptor(target, prop) {
         return Reflect.getOwnPropertyDescriptor(target, prop);
       },

       // Object.defineProperty
       defineProperty(target, prop, descriptor) {
         return Reflect.defineProperty(target, prop, descriptor);
       }
     };
     

Validation example:

     const validator = {
       set(target, prop, value) {
         if (prop === 'age') {
           if (typeof value !== 'number' || value < 0) {
             throw new TypeError('Age must be a positive number');
           }
         }
         target[prop] = value;
         return true;
       }
     };

     const person = new Proxy({}, validator);
     person.age = 30; // OK
     // person.age = -5; // Throws error
     

Property access logging:

     function createLoggingProxy(obj, name = 'object') {
       return new Proxy(obj, {
         get(target, prop) {
           console.log(`${name}.${String(prop)} accessed`);
           const value = target[prop];
           if (typeof value === 'object' && value !== null) {
             return createLoggingProxy(value, `${name}.${String(prop)}`);
           }
           return value;
         }
       });
     }

     const user = createLoggingProxy({ name: 'John', address: { city: 'NYC' } });
     user.address.city; // Logs: object.address accessed, object.address.city accessed
     

Negative array indices:

     function createArray(arr) {
       return new Proxy(arr, {
         get(target, prop) {
           const index = Number(prop);
           if (index < 0) {
             return target[target.length + index];
           }
           return target[prop];
         }
       });
     }

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

Function argument validation:

     function validateArgs(fn, validators) {
       return new Proxy(fn, {
         apply(target, thisArg, args) {
           validators.forEach((validator, i) => {
             if (!validator(args[i])) {
               throw new Error(`Invalid argument at position ${i}`);
             }
           });
           return Reflect.apply(target, thisArg, args);
         }
       });
     }

     const add = validateArgs(
       (a, b) => a + b,
       [
         x => typeof x === 'number',
         x => typeof x === 'number'
       ]
     );

     console.log(add(1, 2)); // 3
     // add('1', 2); // Throws error
     

Candidate Response Strategy & Interview Tips

  1. Start with a concise one-sentence summary: Deliver a direct, confident answer first before expanding into nuances.
  2. Demonstrate real-world trade-offs: Discuss where this approach excels and when you would avoid it in production systems.
  3. Discuss complexity & edge cases: Proactively explain time/space complexity or boundary conditions (null values, scale limits).
  4. Prepare for interviewer follow-ups: Technical hiring panels frequently probe deeper into concurrency, backward compatibility, or alternative libraries.
Related Topics & Skills
Spotted an error or have an alternative solution?