Proxy
Proxy 是 es6 新增的内置对象,它用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
如果传入是数组,则拦截数组操作,如果传入是对象,则拦截对象操作。传入什么,则输出什么,不会像 Set、Map 一样,改变数据结构。
# 创建
语法:new Proxy(target, handler)
var obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});
obj.count = 1; // setting count!
Object.prototype.toString.call(obj) // "[object Object]" 传入什么则输出什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 拦截方法
方法 | 描述 | 返回值 |
---|---|---|
get(target, propKey, receiver) | 拦截对象属性的读取 | 属性值 |
set(target, propKey, value, receiver) | 拦截对象属性的设置 | 布尔值 |
has(target, propKey) | 拦截 in 的操作 | 布尔值 |
defineProperty(target, propKey, propDesc) | 拦截 Object.defineProperty() | 布尔值 |
deleteProperty(target, propKey) | 拦截 delete 操作 | 布尔值 |
ownKeys(target) | 拦截 Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols() | 数组 |
getOwnPropertyDescriptor(target, propKey) | 拦截 Object.getOwnPropertyDescriptor() | 属性的描述对象 |
getPrototypeOf(target) | 拦截 Object.getPrototypeOf() | 对象 |
setPrototypeOf(target, proto) | 拦截 Object.setPrototypeOf() | 布尔值 |
isExtensible(target) | 拦截 Object.isExtensible() | 布尔值 |
preventExtensions(target) | 拦截 Object.preventExtensions() | 布尔值 |
apply(target, object, args) | 拦截函数调用的操作,obj.apply()、obj.call() | |
construct(target, args) | 拦截 new 实例化操作 |
# 优缺点
# Proxy 的优势:
- Proxy 可以直接监听对象而非属性,可以监听新增/删除属性;
- Proxy 可以直接监听数组的变化;
- Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
- Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
# Proxy 不足:
- 兼容性问题,而且无法使用 polyfill 抹平
# Object.defineProperty 的优势:
- 兼容性好,支持 IE9
# Object.defineProperty 不足
- 无法监听数组的变化
- 必须遍历对象的每个属性
- 必须深层遍历嵌套的对象
- 无法监听新增属性、删除属性
- 需要在开始时一次性递归所有属性
# 使用场景
- 运算符重载,
- 对象模拟,
- 简洁而灵活的API创建,
- 对象变化事件
- vue 3.0 的双向绑定
# 双向数据绑定
使用 proxy 实现双向数据绑定
<div>
<h3>数据双向绑定</h3>
<input type="test" id="input">
<div>
输入的内容是:<span id="title"></span> <button type="button" name="button" id="titlebtn">修改input框内容</button>
</div>
<div>
<button type="button" name="button" id="btn">添加到todolist</button>
</div>
</div>
<h3>todo list</h3>
<ul id="list">
</ul>
</div>
<script>
const obj = {};
const arr = [];
const input = document.getElementById("input");
const title = document.getElementById("title");
const titlebtn = document.getElementById("titlebtn")
const list = document.getElementById("list");
const btn = document.getElementById("btn");
// 数据双向绑定
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
if (key === "text") {
input.value = value;
title.innerHTML = value; // 显示
}
return Reflect.set(target, key, value, receiver);
}
});
input.addEventListener("keyup", function(e) {
newObj.text = e.target.value;
});
titlebtn.addEventListener("click", function(e) {
newObj.text = '改变了'
});
// 数组监听
const Render = {
init: function(arr) {
const fragment = document.createDocumentFragment();
for (let i = 0; i < arr.length; i++) {
const li = document.createElement("li");
li.textContent = arr[i];
fragment.appendChild(li);
}
list.appendChild(fragment);
},
addList: function(val) {
const li = document.createElement("li");
li.textContent = val;
list.appendChild(li);
}
};
const newArr = new Proxy(arr, {
get: function(target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
if (key !== "length") {
Render.addList(value);
}
return Reflect.set(target, key, value, receiver);
}
});
// 初始化
window.onload = function() {
Render.init(arr);
};
btn.addEventListener("click", function() {
newArr.push(newObj.text);
});
</script>
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84