# 前言声明

  • 本章大量释义来源于 MDN

# Proxy

# handler.get()

  • handler.get 方法用于拦截对象的读取属性操作。

  • target 目标对象。

  • property 被获取的属性名。

  • receiver Proxy 或者继承 Proxy 的对象

  • get 方法可以返回任何值。

const lain = {
  name: 'lain',
  age: 16
}
const lainProxy = new Proxy(lain, {
  // 获取值的捕获器
  get(target, property) {
    console.log(`lainProxy 的 ${property} 属性被访问了`, target)
    return target[property]
  }
})
//lainProxy 的 name 属性被访问了 {name: 'lain', age: 16}
console.log(lainProxy.name) // lain

# handler.set()

  • **handler.set()** 方法是设置属性值操作的捕获器。
  • target 目标对象
  • property 将被设置的属性名或 Symbol
  • value 新属性值
  • receiver 最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)。
const lain = {
  name: 'lain',
  age: 16
}
const lainProxy = new Proxy(lain, {
  //  设置值的捕获器
  set(target, property, newValue) {
    console.log(`lainProxy 的 ${property} 属性被设置为 ${newValue}`, target)
    target[property] = newValue
  }
})
//lainProxy 的 name 属性被设置为 saber {name: 'lain', age: 16}
lainProxy.name = 'saber'

# handler.has()

  • handler.has() 方法是针对 in 操作符的代理方法。
  • target 目标对象
  • prop 需要检查是否存在的属性.
  • has 方法返回一个 boolean 属性的值.
  • **handler.has** 方法可以看作是针对 in 操作的钩子.
const lain = {
  name: 'lain',
  age: 16
}
const lainProxy = new Proxy(lain, {
  has(target, prop) {
    return prop in target
  }
})
console.log('name' in lainProxy) // true
console.log('friends' in lainProxy) // false

# handler.defineProperty()

  • handler.defineProperty() 用于拦截对对象的 Object.defineProperty() 操作。
  • target 目标对象
  • property 待检索其描述的属性名
  • descriptor 待定义或修改的属性的描述符
  • defineProperty 方法必须以一个 Boolean 返回,表示定义该属性的操作成功与否。
const lain = {
  name: 'lain',
  age: 16
}
const lainProxy = new Proxy(lain, {
  deleteProperty(target, prop) {
    console.log(target, prop) // {name: 'lain', age: 16} 'age'
    delete target[prop]
  }
})
delete lainProxy.age
console.log(lainProxy) // Proxy {name: 'lain'}

# handler.deleteProperty()

  • handler.getPrototypeOf() 是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。
  • target 被代理的目标对象。
  • handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
  • getPrototypeOf 方法的返回值必须是一个对象或者 null
const lain = {
  name: 'lain',
  age: 16
}
const foo = {}
const handel = {
  getPrototypeOf(target) {
    return lainProxy
  }
}
const lainProxy = new Proxy(lain, handel)
console.log(Object.getPrototypeOf(lainProxy) === lain) // false
console.log(Object.getPrototypeOf(lainProxy) === foo) // false
console.log(Object.getPrototypeOf(lainProxy) === lainProxy) // true

# handler.apply()

  • handler.apply() 方法用于拦截函数的调用。
  • target 被代理的目标对象。
  • thisArg 被调用时的上下文对象
  • apply 方法可以返回任何值。
const lain = {
  name: 'lain',
  age: 16
}
function foo() {
  
}
const handler = {
  apply(target, thisArg, argArray) {
    console.log(target, thisArg, argArray) // [Function: foo] {name: 'lain', age: 16} (4) [' 我 ', ' 是 ', ' 参 ', ' 数 ']
    target.apply(thisArg, argArray)
  }
}
const fooProxy = new Proxy(foo, handler)
fooProxy.apply(lain, ['我', '是', '参', '数'])

# handler.construct()

  • handler.construct() 方法用于拦截 new 操作符。
  • target 被代理的目标对象。
  • argumentsList constructor 的参数列表。
  • newTarget 最初被调用的构造函数,就上面的例子而言是 p。
  • construct 方法必须返回一个对象。
function foo() {
  
}
const handler = {
  construct(target, argArray, newTarget) {
    console.log(target, argArray, newTarget) // [Function: foo] [ ' 我 ', ' 是 ', ' 参 ', ' 数 ' ] [Function: foo]
    console.log(newTarget === fooProxy) // true
    return new target(...argArray)
  }
}
const fooProxy = new Proxy(foo, handler)
new fooProxy('我', '是', '参', '数')

# Reflect

# Reflect.get()

  • **Reflect** ** .get() ** 方法与从 对象 ( target[propertyKey] ) 中读取属性类似,但它是通过一个函数执行来操

  • target 需要取值的目标对象

  • propertyKey 需要获取的值的键值

  • receiver 如果 target 对象中指定了 getterreceiver 则为 getter 调用时的 this 值。

  • 返回值 属性的值。

  • Reflect.get 方法允许你从一个对象中取属性值。就如同属性访问器 语法,但却是通过函数调用来实现。

const lain = {
    name: 'lain',
    age: 16
  }
  
  const lainProxy = new Proxy(lain, {
    get(target, key, receiver) {
      console.log('Reflect get') // Reflect get
  
      const returnValue = Reflect.get(target, key, receiver)
      
      console.log(returnValue) // lain
    }
  })
  
  lainProxy.name

# Reflect.set()

  • 静态方法 **Reflect** .set() 工作方式就像在一个对象上设置一个属性。
  • 语法 Reflect.set(target, propertyKey, value[, receiver])
  • · propertyKey 设置的属性的名称。
  • value 设置的值。
  • receiver 如果遇到 setterreceiver 则为 setter 调用时的 this 值。
  • 返回一个 Boolean 值表明是否成功设置属性。
const lain = {
  name: 'lain',
  age: 16
}
const lainProxy = new Proxy(lain, {
   set(target, key, newValue, receiver) {
    console.log('Reflect set') // Reflect set
    const res = Reflect.set(target, key, newValue, receiver)
    
    console.log(res) // true
  }
})
lainProxy.name = 'saber'

# receiver 参数作用

  • 未传 receiver 参数改变 this 之前
const lain = {
  _name: 'lain',
  get name() {
    // 2. 可以看出这里的 this._name 是没有经过代理的,这里的 this 是 lain 对象
    console.log(this === lain) // true
    console.log(this === lainProxy) // false
    return this._name  
  }
}
const lainProxy = new Proxy(lain, {
  get(target, key, receiver) { 
    // 3. 那么这里也就只会触发一次
    console.log('key:', key) // key: name
    // 1. 这里的 Reflect.get 在没有传 receiver 之前,会对 name 属性进行访问,然后会执行上面那么属性的 get
    return Reflect.get(target, key)
  }
})
// 进行访问触发 get
lainProxy.name // lain
  • 传递了 receiver 改变 this 之后
const lain = {
  _name: 'lain',
  get name() {
    // 2. 可以看出这里的 this 已经改变 指向的就是 lainProxy 代理了
    console.log(this === lain) // false
    console.log(this === lainProxy) // true
    // 3. 那么这里的 this._name 也会触发代理 然后再回 lainProxy 里执行一次 get
    return this._name  
  }
}
const lainProxy = new Proxy(lain, {
  get(target, key, receiver) { 
    // 4. 那么这里就会触发两次
    console.log('key:', key) // 这里会触发两次: 1. key: name  2. key: _name
    // 1. 这里的 Reflect.get 在传 receiver 之后,会将 this 改变为 lainProxy,然后会对 name 属性进行访问,然后会执行上面那么属性的 get
    return Reflect.get(target, key, receiver)
  }
})
// 进行访问触发 get
lainProxy.name // lain
  • 那么 set 传入了 receiver 效果也是一样~
const lain = {
  _name: 'lain',
  get name() {
    return this._name  
  },
  set name(newValue) {
    // 3. 可以看出 this 已经改变为 lainProxy
    console.log(this === lainProxy) // true
    // 4. 触发 _name 的 set, 再会到代理进行处理
    this._name = newValue
  }
}
const lainProxy = new Proxy(lain, {
  set(target, key, newValue, receiver) {
    // 4. 这里也会触发两次 代表 name 和_name 都进行了代理
    console.log('set:', key) // 那么这里也会触发两次 1. set: name  2. set: _name
    // 2. 传入 receiver 改变 this 进行代理
    Reflect.set(target, key, newValue, receiver)
  }
})
// 1. 进行设值触发 set
lainProxy.name = 'saber'

# Reflect.construct()

  • **Reflect** .construct() 方法的行为有点像 new 操作符 构造函数 , 相当于运行 new target(...args)
  • **语法 Reflect.construct(target, argumentsList[, newTarget])**
  • target 被运行的目标构造函数
  • argumentsList 类数组,目标构造函数调用时的参数。
  • ``newTarget 可选 作为新创建对象的原型对象的 constructor 属性, 参考 new.target 操作符,默认值为 target。
  • target (如果 newTarget 存在,则为 newTarget )函数为构造函数, argumentList 为其初始化参数的对象实例。
function Foo(name, age) {
  this.name = name
  this.age = age
}
function Bar() {
  
}
const foo = new Foo()
// 要求:执行 Foo 函数中的内容,但是创建出来对象是 Bar 对象
// 
const bar = Reflect.construct(Foo, ['Lain', 16], Bar)
console.log(bar) // Bar { name: 'Lain', age: 16 }
console.log(bar.__proto__ == Bar.prototype) // true