阿里云主机折上折
  • 微信号
Current Site:Index > Basic methods of creating a Proxy

Basic methods of creating a Proxy

Author:Chuan Chen 阅读数:38597人阅读 分类: JavaScript

Basic Concepts of Proxy

The Proxy object is used to define custom behavior for fundamental operations (such as property lookup, assignment, enumeration, function invocation, etc.). It acts as an "interception" layer in front of the target object, where all external access to the object must first pass through this layer.

const target = {};
const handler = {};
const proxy = new Proxy(target, handler);

Basic Syntax for Creating a Proxy

The Proxy constructor takes two parameters:

  1. target: The target object to wrap (can be any type of object, including native arrays, functions, or even another proxy).
  2. handler: An object whose properties are usually functions that define the behavior of the proxy when various operations are performed.
const handler = {
  get(target, prop, receiver) {
    return target[prop] * 2;
  }
};

const obj = { num: 21 };
const proxy = new Proxy(obj, handler);

console.log(proxy.num); // 42

Common Traps in the Handler Object

get Trap

Intercepts property read operations.

const handler = {
  get(target, property) {
    return property in target ? 
      target[property] : 
      `Property "${property}" does not exist`;
  }
};

const proxy = new Proxy({}, handler);
proxy.foo = 'bar';

console.log(proxy.foo); // "bar"
console.log(proxy.baz); // "Property "baz" does not exist"

set Trap

Intercepts property assignment operations.

const validator = {
  set(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('Age must be an integer');
      }
      if (value < 0 || value > 150) {
        throw new RangeError('Age must be between 0 and 150');
      }
    }
    obj[prop] = value;
    return true;
  }
};

const person = new Proxy({}, validator);
person.age = 25; // Works
person.age = 'young'; // Throws TypeError
person.age = 200; // Throws RangeError

apply Trap

Intercepts function calls, call, and apply operations.

const handler = {
  apply(target, thisArg, argumentsList) {
    console.log(`Calling function with args: ${argumentsList}`);
    return target(...argumentsList) * 2;
  }
};

function sum(a, b) {
  return a + b;
}

const proxy = new Proxy(sum, handler);
console.log(proxy(1, 2)); 
// Output: Calling function with args: 1,2
// Output: 6

construct Trap

Intercepts the new operator.

class Person {
  constructor(name) {
    this.name = name;
  }
}

const handler = {
  construct(target, args, newTarget) {
    console.log(`Creating instance with name: ${args[0]}`);
    return new target(...args);
  }
};

const PersonProxy = new Proxy(Person, handler);
const p = new PersonProxy('John'); 
// Output: Creating instance with name: John
console.log(p.name); // "John"

Revocable Proxy Functionality

The Proxy.revocable() method can be used to create a revocable proxy object.

const {proxy, revoke} = Proxy.revocable({}, {
  get(target, prop) {
    return `Intercepted: ${prop}`;
  }
});

console.log(proxy.foo); // "Intercepted: foo"
revoke();
console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revoked

Practical Application Examples

Implementing Data Validation

const userValidator = {
  set(target, prop, value) {
    if (prop === 'email') {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(value)) {
        throw new Error('Invalid email format');
      }
    }
    if (prop === 'password' && value.length < 8) {
      throw new Error('Password must be at least 8 characters');
    }
    target[prop] = value;
    return true;
  }
};

const user = new Proxy({}, userValidator);
user.email = 'test@example.com'; // Works
user.password = '12345678'; // Works
user.email = 'invalid-email'; // Throws Error

Implementing Auto-Filling Objects

const autoFillHandler = {
  get(target, prop) {
    if (!(prop in target)) {
      target[prop] = {};
    }
    return target[prop];
  }
};

const config = new Proxy({}, autoFillHandler);
config.database.host = 'localhost';
config.database.port = 3306;
config.api.endpoint = '/api/v1';

console.log(config);
// Output: { database: { host: 'localhost', port: 3306 }, api: { endpoint: '/api/v1' } }

Implementing Negative Array Indices

const negativeArrayHandler = {
  get(target, prop, receiver) {
    const index = parseInt(prop);
    if (index < 0) {
      prop = String(target.length + index);
    }
    return Reflect.get(target, prop, receiver);
  }
};

function createNegativeArray(array) {
  return new Proxy(array, negativeArrayHandler);
}

const arr = createNegativeArray(['a', 'b', 'c', 'd']);
console.log(arr[-1]); // "d"
console.log(arr[-2]); // "c"

Limitations of Proxy

  1. Cannot proxy primitive values (numbers, strings, booleans, etc.).
  2. Some internal properties of built-in objects cannot be proxied.
  3. Performance overhead is higher than direct object operations.
  4. Cannot modify certain non-configurable properties.
// Attempting to proxy a primitive value throws a TypeError
const proxy = new Proxy(42, {}); // TypeError: Cannot create proxy with a non-object as target

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.