设计模式 ~ 代理

一、概念

为其他对象提供一种代理以控制对这个对象的访问;
直接访问会给使用者或者系统结构带来很多麻烦,在访问此对象时加上一层访问层;
装饰器模式:不能改变原始对象的行为、代理模式:可以改变原始对象的行为(代理和目标都可自行扩展);
设计模式 ~ 代理_第1张图片
常见使用:

const container= document.getElementById('container')
container.addEventListener('click', function (e) {
  const target = e.target
  if (target.nodeName === 'A') {
    // ... do something
  }
})

以上为DOM 事件代理( 也叫事件委托 );
正向代理:webpack-dev-server ~ proxy ;
nginx 反向代理、客户端只需要根据服务端给定端口进行访问,而服务端自行判断;

二、理解

例如、你通过房产中介买房子,中介就是一个代理、你接触到的是中介这个代理,而非真正的房主;

// 户主
class master {
  sign() {
    console.log('户主签字')
  }
}
// 代理
class agent {
  constructor() {
    this.master = new master()
  }
  // 签约
  sign() {
    console.log('按指纹')
    console.log('交定金')
    console.log('顾客签字')
    // ... 限制
    this.master.sign()
  }
}
const proxy = new agent()
proxy.sign()

例如、明星和经纪人,主办方需要联系经纪人进行签约,使用 proxy

const star = {
  name: 'iu',
  age: 18,
  phone: '****', // 号码
  price: 0 // 出场费
}
const min = 100 * 1000 // 最少出场费
// 经纪人
const agent = new Proxy(star, {
  get(target, key) {
    if (key === 'phone') {
      return '13988888888' // 经纪人电话
    } else if (key === 'price') {
      const now = Reflect.get(target, key)
      return now > min ? now : min
    } else {
      return Reflect.get(target, key)
    }
  },
  set(target, key, val) {
    if (key === 'price') {
      return Reflect.set(target, key, val)
    }
    return false
  }
})
console.log(agent.phone) // 13988888888
console.log(agent.price) // 100000
agent.price = 100 * 2000
console.log(agent.price) // 200000

三、使用场景

跟踪属性访问: Vue3 响应式实现原理

const user = {
  name: '张三'
}
const proxy = new Proxy(user, {
  get(...args) {
    return Reflect.get(...args)
  },
  set(target, key, val) {
    return Reflect.set(target, key, val)
  }
})
proxy.name = '李四'
console.log(proxy.name) // '李四'

隐藏属性: 拒绝某些属性的访问和设置

// 隐藏Key
const hiddenProps = ['girlfriend']
const user = {
  name: '张三',
  girlfriend: '李红'
}
const proxy = new Proxy(user, {
  get(target, key) {
    if (hiddenProps.includes(key)) return undefined
    return Reflect.get(target, key)
  },
  has(target, key) {
    if (hiddenProps.includes(key)) return false
    return Reflect.has(target, key)
  }
})

console.log('girlfriend', proxy.girlfriend) // undefined
console.log('girlfriend' in proxy) // false

验证: 建议使用 TS

const user = {
  name: '张三',
  age: 25
}
const proxy = new Proxy(user, {
  get(target, key) {
    return Reflect.get(target, key)
  },
  set(target, key, val) {
    if (key === 'age') {
      if (typeof val !== 'number') return false // 验证 age 类型
    }
    return Reflect.set(target, key, val)
  }
})
proxy.age = '18'
console.log(proxy.age) // 25

记录实例

const userList = new WeakSet() // 记录表 ~ 每次初始化 user 进行记录
class User {
  constructor(name) {
    this.name = name
  }
}
const ProxyUser = new Proxy(User, {
  construct(...args) {
    const user = Reflect.construct(...args)
    userList.add(user) // 记录
    return user
  }
})
const user1 = new ProxyUser('张三')
const user2 = new ProxyUser('李四')
console.log(userList.has(user1)) // true

四、注意事项

捕获器不变式: 代理无法改变对象本身原有的属性设置

const obj = { x: 100, y: 0 }
Object.defineProperty(obj, 'y', {
    value: 200,
    writable: false,
    configurable: false,
})
const proxy = new Proxy(obj, {
    get() {
        return 'abc'
    }
})

console.log(proxy.x)
console.log(proxy.y) // y 属性描述符被修改,proxy 不能修改它的值

this: 函数里的 this 是由执行时确认的,而非定义时。

const user = {
  name: '张三',
  getName() {
    console.log('this...', this)
    return this.name
  }
}
const proxyUser = new Proxy(user, {})
user.getName() // this 是 user
proxyUser.getName() //this 是 proxy

你可能感兴趣的:(javascript,设计模式)