ES6 Proxy 应用场景 -4

一、核心应用场景

1. 属性访问跟踪

场景:记录对象属性的读取/修改记录
实现

const trackable = (obj) => new Proxy(obj, {
  get(target, key) {
    console.log(`[GET] ${key}`)
    return Reflect.get(target, key)
  },
  set(target, key, value) {
    console.log(`[SET] ${key} = ${value}`)
    return Reflect.set(target, key, value)
  }
})

// 使用
const user = trackable({ name: 'Alice' })
user.name // 控制台输出 [GET] name
user.age = 30 // 控制台输出 [SET] age = 30

2. 属性隐藏

场景:实现私有属性(约定以 _ 开头)
实现

const createPrivateProxy = (obj) => new Proxy(obj, {
  has(target, key) {
    if (key.startsWith('_')) return false
    return key in target
  },
  ownKeys(target) {
    return Reflect.ownKeys(target).filter(k => !k.startsWith('_'))
  }
})

// 使用
const data = createPrivateProxy({ 
  public: 'visible', 
  _secret: 'hidden' 
})

console.log('_secret' in data) // false
console.log(Object.keys(data)) // ["public"]

3. 属性验证

场景:强制数据类型校验
实现

const validated = (schema) => ({
  set(target, key, value) {
    if (schema[key] && typeof value !== schema[key]) {
      throw new Error(`Invalid type for ${key}`)
    }
    return Reflect.set(target, key, value)
  }
})

const userProxy = new Proxy({}, validated({ 
  age: 'number', 
  name: 'string' 
}))

userProxy.age = 30 // 正常
userProxy.name = 123 // 抛出错误

4. 函数参数验证

场景:验证函数参数类型
实现

const validateArgs = (fn, check) => new Proxy(fn, {
  apply(target, thisArg, args) {
    args.forEach((arg, i) => {
      if (typeof arg !== check[i]) {
        throw new Error(`参数 ${i+1} 类型错误`)
      }
    })
    return Reflect.apply(target, thisArg, args)
  }
})

// 使用
const sum = validateArgs(
  (a, b) => a + b,
  ['number', 'number']
)

sum(1, 2) // 3
sum('1', 2) // 报错

5. 数据绑定

场景:DOM 自动同步更新
实现

const bindDOM = (selector) => {
  const el = document.querySelector(selector)
  return new Proxy({}, {
    set(target, key, value) {
      el.textContent = value
      return Reflect.set(target, key, value)
    }
  })
}

// 使用
const title = bindDOM('#title')
title.text = 'Hello Proxy!' // 自动更新 DOM

二、进阶应用场景

1. 缓存代理

场景:缓存函数计算结果

const createCacheProxy = (fn) => {
  const cache = new Map()
  return new Proxy(fn, {
    apply(target, thisArg, args) {
      const key = JSON.stringify(args)
      if (cache.has(key)) {
        console.log('返回缓存结果')
        return cache.get(key)
      }
      const result = Reflect.apply(target, thisArg, args)
      cache.set(key, result)
      return result
    }
  })
}

// 使用
const heavyCalc = (n) => n * 2
const cachedCalc = createCacheProxy(heavyCalc)

cachedCalc(5) // 计算
cachedCalc(5) // 控制台显示"返回缓存结果"

2. 权限控制

场景:基于角色限制访问

const createGuard = (obj, role) => new Proxy(obj, {
  get(target, key) {
    if (key === 'adminData' && role !== 'admin') {
      throw new Error('权限不足')
    }
    return Reflect.get(target, key)
  }
})

// 使用
const data = createGuard({ 
  public: 'info', 
  adminData: 'secret' 
}, 'user')

console.log(data.public) // "info"
console.log(data.adminData) // 报错

3. 自动 ORM 映射

场景:数据库字段动态映射

class UserModel {
  constructor(data) {
    return new Proxy(data, {
      get(target, key) {
        if (key === 'fullName') {
          return `${target.firstName} ${target.lastName}`
        }
        return Reflect.get(target, key)
      }
    })
  }
}

// 使用
const user = new UserModel({
  firstName: 'John',
  lastName: 'Doe'
})

console.log(user.fullName) // "John Doe"

三、创新应用场景

1. 模式匹配

场景:实现类似 Rust 的模式匹配

const match = (pattern) => new Proxy({}, {
  get(_, key) {
    return (value) => {
      if (typeof pattern === 'function' ? pattern(value) : pattern === value) {
        return { [key]: value }
      }
      return null
    }
  }
})

// 使用
const check = match({
  status: 200,
  data: (d) => d.length > 0
})

const res = { status: 200, data: [1,2,3] }
console.log(check.success(res)) // { success: res }

2. 不可变数据

场景:创建不可变对象

const immutable = (obj) => new Proxy(obj, {
  set() { throw new Error('不可修改') },
  deleteProperty() { throw new Error('不可删除') }
})

// 使用
const config = immutable({ key: 'abc123' })
config.key = 'new' // 抛出错误

3. 链式调用优化

场景:优化 Lodash 式链式调用

const chainable = (obj) => new Proxy({}, {
  get(_, method) {
    return (...args) => {
      obj = method === 'value' ? obj : obj[method](...args)
      return chainable(obj)
    }
  }
})

// 使用
const _ = chainable([1,2,3])
  .filter(x => x > 1)
  .map(x => x * 2)
  .value()

console.log(_) // [4,6]

四、Proxy 应用全景图

graph LR
A[Proxy 核心应用] --> B[元编程]
A --> C[数据拦截]
A --> D[行为扩展]

B --> B1(调试工具)
B --> B2(DSL实现)
B --> B3(模式匹配)

C --> C1(响应式系统)
C --> C2(数据校验)
C --> C3(访问控制)

D --> D1(链式优化)
D --> D2(缓存代理)
D --> D3(延迟加载)

classDef highlight fill:#f9f,stroke:#333;
class B1,B2,B3,C1,C2,C3,D1,D2,D3 highlight

关键知识点总结

  1. 陷阱方法选择
    根据场景选择正确的拦截器(如数据验证用 set,函数拦截用 apply

  2. Reflect 的必要性
    始终使用 Reflect 方法保持默认行为:

     
    // 正确做法
    set(target, key, value) {
      // 自定义逻辑
      return Reflect.set(...arguments)
    }
  3. 性能注意事项
    避免深层代理嵌套,对于性能敏感场景使用对象池:

     
    const proxyCache = new WeakMap()
    function createProxy(obj) {
      if (proxyCache.has(obj)) return proxyCache.get(obj)
      const proxy = new Proxy(...)
      proxyCache.set(obj, proxy)
      return proxy
    }
  4. 浏览器兼容方案
    通过 @babel/plugin-proxy 实现 IE11 兼容:

     
    npm install @babel/plugin-proxy

通过以上案例可以看出,Proxy 为 JavaScript 带来了强大的元编程能力,能够优雅地解决许多传统编程模式难以处理的问题。这些模式不仅提升了代码的可维护性,也为实现复杂系统架构提供了新的可能性。

posted @ 2025-02-10 14:53  Yang9710  阅读(56)  评论(0)    收藏  举报