es6中的proxy的使用

# 简介

proxy是ES6,新增的一个“拦截器”,也可以理解成是ES6,新增的一种元变编程功能。

# 作用

proxy用于修改某些操作的默认行为,等同于在语言层面作出修改。

let proxy = new Proxy({},{
    get:function(target,property){
        return 35
    }
})
proxy.xxx //35
1
2
3
4
5
6

「Proxy构造函数的解释」

Proxy构造函数接受两个参数,第一个参数是所要代理的目标对象;第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。

# API

# 1.get(target,propKey,receiver)

拦截对象属性的读取,target是被代理的对象,propKey是需要拦截的键,receiver是一个可选的参数。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    get: function (target, key) {
        console.log(target, key)
        return target[key]
    }
})
console.log(proxy.name)
1
2
3
4
5
6
7
8
9
10

# 2.set(target,propKey,value,receiver)

拦截对象属性的设置,target是被代理的对象,propKey是需要拦截的键,value是要设置的值,receiver是一个可选的参数。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    set: function (target, key, value) {
        return target[key] = value
    }
})
proxy.name = 'samyzh'
console.log(proxy.name)
1
2
3
4
5
6
7
8
9
10

# 3.has(target,propKey)

拦截propKey in target的操作,返回一个布尔值。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    has:function(target,key){
        console.log(target,key)
        return key in target
    }
})
console.log('name' in proxy)
1
2
3
4
5
6
7
8
9
10

如果原对象不可配置或者禁止扩展,那么这是has拦截会报错;还有has方法拦截的是HasProperty,而不是HasOwnProperty, 所以has方法不判断一个属性是对象自身还是继承的属性。

# 4.deleteProperty(target,propKey)

拦截 delete target[propKey]的操作,返回一个布尔值。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    deleteProperty: function (target, key) {
        console.log(target, key)
        if(!(key in target)) throw TypeError('不存在的key')
        return delete [target, key]

    }
})
console.log(delete proxy['name'])
1
2
3
4
5
6
7
8
9
10
11
12

目标对象自身的不可配置的属性不能被deleteProperty方法删除,否则会报错。

# 5.ownKeys(target)

拦截Object.getOwnPropertyNames(proxy),Object.getOwnPropertySymbols(proxy),Object.keys(proxy),返回一个数组,该方法返回目标对象所以自身属性的属性名。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    ownKeys:function(target){
        console.log(target)
        return ['name']
    }
})

console.log(Object.keys(proxy))
1
2
3
4
5
6
7
8
9
10
11

ownKeys方法返回的数组成员只能是字符串或者Symbol值,如果是其他类型的值,或者返回的根本不是数组,就会报错;如果目标对象自身包含不可配置的属性,则该属性必须ownKeys方法返回,否则也会报错;如果目标对象是不可扩展的,这时ownKeys方法返回的数组中必须包含原对象的所以属性,且不能包含多余的属性,否则也会报错。

# 6.getOwnPropertyDescriptor(target,propKey)

拦截Object.getOwnPropertyDescriptor(proxy,propKey),返回属性的描述对象。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    getOwnPropertyDescriptor:function(target,key){
        console.log(target,key)
        return Object.getOwnPropertyDescriptor(target,key)
    }
})
console.log(Object.getOwnPropertyDescriptor(obj,'name'))
1
2
3
4
5
6
7
8
9
10

# 7.defineProperty(target,peopKey,propDesc)

拦截Object.defineProperty(proxy,propKey,propDesc),Object.defineProperties(proxy,propDesc),返回一个布尔值。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    defineProperty:function(target,key,desc){
        console.log(target,key,desc)
        return true
    }
})
console.log(Object.defineProperty(proxy,'name',{}))
1
2
3
4
5
6
7
8
9
10

如果目标对象不可扩展,则defineProperty不能增加目标对象中不存在的属性,否则会报错,另外,如果目标对象的某个属性不可写或者不可配置,则defineProperty方法不得改变这两个设置。

# 8.preventExtensions(target)

拦截Object.preventExtensions(proxy),返回一个布尔值。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    preventExtensions:function(target){
        console.log(target)
        Object.preventExtensions(target)
        return true
    }
})
console.log(Object.preventExtensions(proxy))
1
2
3
4
5
6
7
8
9
10
11

这个方法有一个限制,只有目标对象不可扩展(既Obeject.preventExtensions(proxy)为false)proxy.preventExtensions才能返回true,否则会报错。

# 9.getPrototypeOf(target)

拦截Object.getPrototypeOf(proxy),返回一个对象。具体拦截如下👇

  • Object.prototype.「proto」
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof
let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    getPrototypeOf:function(target){
        console.log(target)
        return obj
    }
})
console.log(Object.getPrototypeOf(proxy))
1
2
3
4
5
6
7
8
9
10

getPrototypeOf方法的返回值必须是对象或者是null,否则会报错。另外,如果目标对象不可扩展,getPrototypeOf方法必须返回目标对象的原型对象。

# 10.isExtensible(target)

拦截Object.isExtensible(proxy),返回一个布尔值。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    isExtensible:function(target){
        console.log(target)
        return true
    }
})
console.log(Object.isExtensible(proxy))
1
2
3
4
5
6
7
8
9
10

该方法只能返回布尔值,否则返回的值会被自动转为布尔值。

# 11.setPrototypeOf(target,proto)

拦截Object.setPrototypeOf(proxy,proto),返回一个布尔值。

let obj = {
    name: "samy"
}
let proxy = new Proxy(obj, {
    setPrototypeOf:function(target,proto){
        console.log(target,proto)
        target.prototype = proto
        return true
    }   
})
console.log(Object.setPrototypeOf(proxy,obj))
1
2
3
4
5
6
7
8
9
10
11

该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展,setPrototypeOf方法不得改变目标对象的原型。

# 12.apply(target,object,args)

拦截Proxy实例,并将其作为函数调用的操作,比如proxy(...args)、proxy.call(object,...args)、proxy.apply(object,...args)。

let proxy = new Proxy(Person, {
    apply:function(target,ctx,args){
        console.log(target,ctx,args)
        return 'i am a boy'
    }
})
function Person (){
    return 'i am a gril'
}
console.log(proxy())
1
2
3
4
5
6
7
8
9
10

# 13.construct(target,args)

拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)。

let proxy = new Proxy(Person, {
    construct:function(target,args){
        console.log(target,args)
        return {value:'18'}
    }
})
function Person (){
    return 'i am a gril'
}
console.log(new proxy().value)
1
2
3
4
5
6
7
8
9
10

construct方法必须返回一个对象,否则会报错。

# 完整代码👇[要点]

let proxy = new Proxy({}, {
    get: function (target, key) {
        console.log(target, key)
        return target[key]
    },
    set: function (target, key, value) {
        return target[key] = value
    },
    has: function (target, key) {
        console.log(target, key)
        return key in target
    },
    deleteProperty: function (target, key) {
        console.log(target, key)
        if(!(key in target)) throw TypeError('不存在的key')
        return delete [target, key]
    },
    ownKeys:function(target){
        console.log(target)
        return ['name']
    },
    getOwnPropertyDescriptor:function(target,key){
        console.log(target,key)
        return Object.getOwnPropertyDescriptor(target,key)
    },
    defineProperty:function(target,key,desc){
        console.log(target,key,desc)
        return true
    },
    preventExtensions:function(target){
        console.log(target)
        Object.preventExtensions(target)
        return true
    },
    getPrototypeOf:function(target){
        console.log(target)
        return obj
    },
    isExtensible:function(target){
        console.log(target)
        return true
    },
    setPrototypeOf:function(target,proto){
        console.log(target,proto)
        target.prototype = proto
        return true
    },
    apply:function(target,ctx,args){
        console.log(target,ctx,args)
        return 'i am a boy'
    },
    construct:function(target,args){
        console.log(target,args)
        return {value:'18'}
    }
})
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 注意问题

# this问题

Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理,既不做任何拦截的情况下也无法保证于目标对象的行为一致。主要原因就是Proxy代理的情况下,目标对象内部的this关键字会指向Proxy代理

const target ={
    m:function(){
        console.log(this === proxy)
    }
}
const proxy = new Proxy({},{})
target.m() // false
proxy.m() // true
1
2
3
4
5
6
7
8

# 参考资料

  • 《es6标准入门》
上次更新: 2022/04/15, 05:41:26
×