E3到ES6(proxy)实现对象代理(数据保护)

首先提出一个问题:假如你现在需要封装一个数据对象,这个数据呢,只能让内部的方法访问到,其他的api是无法访问到的,你会怎么做?

我们知道,对于Java,C#等语言,它们都存在私有属性(private),限制只有对象(或者类)内部的方法才能访问到。但是对于ES6之前的javascript,虽然也可以模拟实现,但是会比较麻烦,直到ES6中的proxy出现后,才简单了许多。下面我们来看看ES3-ES6是怎么实现对象代理(数据保护)的:

以下代码范例都是以保护一个数据对象中的 sex 属性为例!

es3

先看看 ES3 的实现方式,基于构造函数 Person 中的局部作用域实现:

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
var Person = function(){

var data = {
name:"es3",
sex: "male",
age:15
}

this.get = function(key){
return data[key];
}

this.set = function(key,value){
if(key !== "sex"){
data[key] = value;
}
}

}

var person = new Person();
//读取
console.table({
name: person.get("name"),
sex: person.get("sex"),
age: person.get("age")
});
//修改
person.set("name","es3-change");
person.set("sex","female");
person.set("age",18);
//在次读取
console.table({
name: person.get("name"),
sex: person.get("sex"),
age: person.get("age")
});

我们看看输出结果:

从上图可以看出,sex 属性是保护的,所以修改是不成功的,name 和 age 属性被成功修改。

可以看出,这样只写是炒鸡麻烦的~

es5

我们再看看 ES5 中的实现方式,基于 Object.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
var Person = {
name: "es5",
age: 16
}
//定义保护属性sex
Object.defineProperty(Person,'sex',{
wriable: false,
value: "male"
});
//读取属性
console.table({
name:Person.name,
sex:Person.sex,
age:Person.age
});
//修改
Person.name = "es5-change";
Person.sex = "female";
Person.age = 18;
//在次读取
console.table({
name:Person.name,
sex:Person.sex,
age:Person.age
});

我们看看输出结果:

可以看出,ES5中的实现方式比ES3中简单了许多,但是还不够,因为当涉及保护的数据非常多的时候,这样写还是比较麻烦,而且不能设定规则,要么只读,要么可写,非常绝对,没办法让它什么情况下只可读,什么情况下可写等。

es6

最后看看ES6中的实现方式,基于proxy:

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
let Person = {
name:"es6",
sex: "male",
age: 10
}

//定义代理对象person操作对象Person,且只能通过代理对象person操作对象Person
let person = new Proxy(Person,{
get(target,key){
return target[key]
},
set(target,key,value){
if(key !== "sex"){//设置写的保护逻辑
target[key] = value
}
}
});
//读取
console.table({
name:person.name,
sex:person.sex,
age:person.age
});
//修改
person.name = "es6-change";
person.sex = "female";
person.age = 18;
//再次读取
console.table({
name:person.name,
sex:person.sex,
age:person.age
});

我们同样看看输出结果:

可以看到,我们通过 Proxy 让新的对象 person 代理需操作的 Person 对象,只暴露 person 对象给外部进行读写和修改,并且在 get 和 set 方法中进行逻辑的控制、限制等,既不影响原来的对象 person,又跟业务逻辑隔离,是个非常棒的原生方法。



完~