ES6常见

proxy

‘代理’,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法。

基础用法

1
2
3
4
5
6
let pro = new Proxy(target, handler)
/*
Proxy 是es6提供的原生构造函数
target 表示目标对象(即被代理的对象)
handler 也是一个对象,控制拦截行为的对象,其中包含get,set,apply等控制方法
*/
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
let target = {
name: 'gss',
age: 18,
hob: {
a: [1,2,3],
b: [3,4,5],
c: [4,5,6]
}
}

let handler = {
get: (target, prop) => {
console.log('getter-'+prop, typeof target[prop]);
if (typeof target[prop] === 'object' && target[prop] !== null) {
return new Proxy(target[prop], handler) // proxy能代理整个对象,但深层也需要递归
}
return target[prop]
},
set: (target, prop, val) => {
console.log(val);
target[prop] = val
// return true
}
}

let pro = new Proxy(target, handler)

console.log(pro.name)
pro.age = 29
pro.sex = 'man' // 新增属性也能拦截到
console.log(pro.hob.a);
pro.hob.a = [1,1,1]
将proxy当作其他对象的原型对象,其中属性能得到继承
1
2
let o = Object.create(pro)
o.age

handler 中的拦截操作(13种)

  • get(target, propKey, receiver)
  • set(target, propKey, value, receiver)
  • apply(target, object, args)
  • construct(target, args)
  • has(target, propKey)
  • ownKeys(target)
  • defineProperty(target, propKey, propDesc)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let fn = function (a,b) {
console.log(a,b);
return a+b
}

let handler = {
get: function (target, prop) {
return 'getter-' + prop
},
apply: function (target, bindThis, args) {
return args[0]
},
construct: function (target, args) {
return {val: args[1]} // 拦截构造函数必须返回对象
}
}

let pro = new Proxy(fn, handler)

fn(0,0) // 0,0
console.log(pro(1,2)); // 1
console.log(new pro(3,4)); // {val: 4}
console.log(pro.a); // getter-a

this问题

代理之后的this是指向新对象的,这就导致了一些问题,比如一些原生对象(date)改变this会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let d = new Date()
let h = {
get: function (target, prop) {
// return target[prop]
if (prop === 'getDate') {
return target.getDate.bind(target)
}
return Reflect.get(target, prop)
}
}
let dd = new Proxy(d, h)

console.log(dd.getDate()); // 不做处理会报错
console.log(dd.ge); // undefined

Reflect

需要在Proxy内部调用对象的默认行为,该如何实现?Reflect即可,它也有13种方法对应handler

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)
1
2
3
4
5
6
7
8
9
10
11
12
13
let handler = {
get: function (target, prop) {
return Reflect.get(target, prop)
},
apply: function (target, bindThis, args) {
// return args[0]
return Reflect.apply(target,bindThis,args)
},
construct: function (target, args) {
return Reflect.construct(target, args)
}
}
// 函数式调用即可

WeakMap

详解

  • 弱映射
  • 对象为键
  • 不阻碍垃圾回收
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const wm = new weakMap()
    vm.set({}, 'val') // 键为对象,没有其他引用,所以执行完就会被垃圾回收

    const obj = {
    k: {}
    }
    vm.set(obj.k, 'val') // 此键有其他引用,因此不会被回收

    obj.k = null // 销毁引用后,将被回收

Object.freeze()

参考:

proxy
深入实践 ES6 Proxy & Reflect