js代理

1、使用代理控制访问

​ 代理是ES6语法提出的,是我们通过代理控制对另一个对象的访问。可以将代理理解为通用化的setter和getter方法,区别就是每个setter和getter只能控制单个对象的属性,而代理可用于对象交互的通用处理,包括调用对象的方法。首先先看下面的这个例子。

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
const people = {
name:'liming'
}
const father = new Proxy(people,{
get:(target,key) => {
console.log(target[key])
if(key in target)
return target[key]
else{
console.log('The value do not exits');
return undefined;
}
},
set:(target,key,value) => {
console.log('Something changed');
target[key] = value;
}
})

console.log(father.name)
father['name'] = "mingming"
console.log(people.name)
console.log(father.ta)

// 执行结果
// liming
// liming
// Something changed
// mingming
// undefined
// The value do not exits
// undefined

这里我们定义了一个people对象,并设置了他的代理father,并且在father中指定了getter和setter方法。首先先查看father的名称,这时候会触发father的getter并返回名称。如果这里在getter中不返回,在console.log返回将会是undefined。之后对father进行了赋值,调用了father的setter方法,并将target即people的name属性进行了重新设定。之后又对原来people中不存在的一个变量ta进行访问,这时候还会去调用getter方法,然后检查key不在target对象中的时候就会返回一个undefined。

2、使用代理的优化代码

简单的说明

使用代理同时还可以优化代码。在不使用代理的时候,对一个对象的设置需要进行很多次的defineProperty的设置,如果需要为一个对象添加很多属性的时候就显得很多。但是利用了代理就能够很方便的解决这些问题。

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
function makeConstructor(target){
return new Proxy(target,{
get:(target,property) =>{
console.log('Get function invoked')
return target[property];
},
set:(target,property,value) => {
console.log('Set function invoked');
target[property] = value;
}
})
}
let peo = {name:'Yoshi'}
peo = makeConstructor(peo);
console.log('分割线1')
console.log(peo.name)
console.log('分割线2')
peo.weapon = 'shui'
console.log('分割线3')
console.log(peo.weapon)
console.log('分割线4')
console.log(peo)

// 运行结果:
// 分割线1
// Get function invoked
// Yoshi
// 分割线2
// Set function invoked
// 分割线3
// Get function invoked
// shui
// 分割线4
// Get function invoked
// Get function invoked
// Get function invoked

首先定义了一个构造器,使用了一个对象target作为被代理的对象。然后调用了makeConstructor来构造对象和代理之间的关系,并在makeConstructor返回了新建的代理,并设置了相关的一些方法。然后可以发现,最开始对象只有一个name属性,在访问的时候会触发getter方法并返回它的值。之后对peo对象中之前不存在的一个属性weapon进行了设置,这时候可以看见触发了setter方法,并在对象中添加了一个名为”weapon”属性。之后又查看了刚刚添加的weapon方法。在最后可以看见,我们console.log输出peo这个对象,结果发现调用了三次get方法。为了探究我们对get方法进行一些处理。

node中运行代码的不同探究

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
function makeConstructor(target){
return new Proxy(target,{
get:(target,property) =>{
//console.log(property)
console.log('Get function invoked')
return target[property];
},
set:(target,property,value) => {
target[property] = value;
}
})
}

let peo = {name:'Yoshi'}
peo = makeConstructor(peo);
peo.weapon = 'shui'
peo.weapon1 = 'shui'
peo.weapon2 = 'shui'
console.log(peo)

// //运行的结果:
// Get function invoked
// Get function invoked
// Get function invoked
// { name: 'Yoshi',
// weapon: 'shui',
// weapon1: 'shui',
// weapon2: 'shui' }

刚开始我的理解是在输出peo的时候,首先对peo这个类整体的访问会触发一次getter,之后对他里面的两个属性name和weapon访问取值的又是两次调用getter,这样就恰好三次。但是当我再增加几个属性的时候结果还是三次。然后就在get中每次输出property,可以看到下面的几个输出:

1
2
3
Symbol(util.inspect.custom)
inspect
Symbol(Symbol.iterator)

原因解释

​ 因为我是在node里面运行这个代码的,所以console.log出来的实际上是将这个对象进行了处理输出了对象中的属性。实际上在chrome浏览器的控制台中输入的话运行出来的结果应该是一个代理的对象。然后由于在node中对对象进行了一些处理,所以难免会对get进行调用从而将对象转化成可视的字符串类型。以上这些代码实际上都是对这个对象进行的一系列的操作。是node中util自带的一些模块,就是为了弥补JavaScript中原始方法的不足。


3.使用代理实现自动的填充属性

当我们拥有一个对象具有很长一串属性的时候,通常我们可以通过代理来生成它没有的属性,避免带妈妈的重复和繁重。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Folder(){
return new Proxy({},{
get:(target,property)=>{
if(!(property in target)){
target[property] = new Folder();
}
return target[property];
}
})
}
const myFile = new Folder();
try{
myFile.root.first.second.next = "aa.txt";
}catch(e){
console.log('error')
}

在try中虽然myFile并没有后面的一系列属性,但当我们读取的时候会调用代理中的get方法帮我们创建,这样的话即使这个属性不存在也不会出现undefined的问题。


4.使用代理实现数组的负索引

​ 在python等语言中,是允许使用负索引来查找对象的,但是JavaScript不允许这样做,但是通过代理的方法我们可以来模拟这个过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function createArray(array){
return new Proxy(array,{
get:(target,index) =>{
index = +index
return array[index < 0 ? target.length + index : index];
},
set:(target,index,value) => {
index = +index
return array[index < 0 ? target.length + index : index] = value
}})
}

const test = ['hh','sss','llll','iii']
const test2 = createArray(test)
console.log(test2[-1]);
console.log(test2[-3]);
console.log(test[-1])
//执行的结果是
// iii
// sss
// undefined

这里通过一元运算符+将属性名变成了数值。然后在代理中对他们的负数情况进行了相应的处理。然后新建一个test的数组,并创建它的代理,通过代理可以用负索引来访问数组中的值。


5.代理的性能消耗

相比较正常访问对象而言,使用代理去访问性能会慢很多,比如上个例子中,使用代理访问数组相互比较正常访问数组而言。在Chrome上时间为正常的50倍,所以使用代理的时候还是需要谨慎。如果是在多性能不敏感的情况下使用。

0%