代理
# 什么是代理
一个对象提供一个代用品,以便控制对它的访问
# Object.defineProperty
- 使用上述该语法可以实现对象的代理。
 Object.defineProperty(obj, key, descriptor).但是与其说是代理,不如说是重写。- 因为使用该 API 进行代理后,会修改原对象的属性。如果使用了 getter/setter 的话 给原对象设置 get/ set 方法。所以可以说是重写/ 不是代理
 
- 使用该 API 可以配置一些属性。例如是否可配置/ 是否枚举等。同时可以对属性进行代理 getter/ setter. 当对属性进行获取/ 设置的时候,可以进行监听触发。
 - 但是对对象重写/ 数组重写的时候 略有不同
 
const obj = { name: '', arr: [] }
function cloneObj(obj, key) {
  let val = obj[key]
  // 对象
  Object.defineProperty(obj, key, {
    get() {
      console.log('进行name属性值获取')
      return val
    },
    set(v) {
      console.log(`修改了属性name,结果变成了${v}`)
      val = v
    }
  })
}
function cloneArray(obj, key) {
  let val = obj[key]
  Object.defineProperty(obj[key], '0', {
    get() {
      return val
    },
    set(v) {
      document.getElementById('content').innerHTML = v
      console.log(v)
    }
  })
}
cloneObj(obj, 'name')
cloneArray(obj, 'arr')
obj.name
console.log(obj)
obj.name = 'test'
document.getElementById('btn').addEventListener('click', () => {
  obj.arr.push(0)
})
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

- 对象代理属性后,会出现上述截图中的两个方法
 - 进行代理后,获取属性值的时候,会触发
get方法。进行设置值的时候会触发set方法。 其实对对象的代理还是相对完美的。只不过是需要通过遍历一个一个进行代理。如下: 
const obj = { name: 'xxx', age: 20 }
for (const key in obj) {
  Object.defineProperty(obj, key, {
    get() {},
    set(v) {}
  })
}
 1
2
3
4
5
6
7
2
3
4
5
6
7
- 如果是代理数组的话,Object.defineProperty 就不是那么友好了
 - 对于数组的 api 例如:push.以及修改 length 长度。是无法监听到的
 - 只能通过
arr[0] = 1这种方式 才会触发监听。而且数组长度很大的话。如果代理下标,很浪费性能。所以框架vue2中对数组的监听。是通过函数劫持来完成的 
# 兼容性

# Proxy
- 是 es6 中新语法。可以监听多个动作变化,除了 set/ get/ has/ del 等 多达 13 中变化
 - 是对 对象进行代理。会返回一个代理后的对象。原对象保持不变
 - 在代理数组的时候,可以监听到 push/ length 的函数/ 属性的调用
 - 唯一的缺陷就是兼容性 不如 Object.defineProperty API
 
const obj = {
  name: 'xxx',
  arr: []
}
const proxyObj = new Proxy(obj, {
  get(target, key, receiver) {
    console.log(`触发了属性${key}`)
    const res = Reflect.get(target, key, receiver)
    if (res && typeof res !== 'object') return res
    return new Proxy(res, {
      get(target, key, receiver) {
        console.log(`触发了属性${key}`, 'child')
        return Reflect.get(target, key, receiver)
      },
      set(target, key, value, receiver) {
        console.log(`触发了属性${key}, 设置内容是${value}`, 'child')
        Reflect.set(target, key, value, receiver)
      }
    })
  },
  set(target, key, value, receiver) {
    console.log(`触发了属性${key}, 设置内容是${value}`)
    Reflect.set(target, key, value, receiver)
  }
})
console.log(proxyObj.name)
proxyObj.name = 'lihh1'
proxyObj.arr.length = 0
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32