105 lines
3.0 KiB
Markdown
105 lines
3.0 KiB
Markdown
|
# JavaScript 数据代理实现文档
|
|||
|
|
|||
|
## 一、基本概念
|
|||
|
|
|||
|
数据代理是指通过一个对象(代理对象)间接访问和操作另一个对象(目标对象)的属性。本示例展示了如何使用 `Object.defineProperty` 实现简单的数据代理。
|
|||
|
|
|||
|
## 二、代码实现解析
|
|||
|
|
|||
|
```javascript
|
|||
|
let obj1 = { x: 100 }; // 目标对象(被代理对象)
|
|||
|
let obj2 = { y: 200 }; // 代理对象
|
|||
|
|
|||
|
// 在obj2上定义x属性的代理
|
|||
|
Object.defineProperty(obj2, "x", {
|
|||
|
get() {
|
|||
|
return obj1.x; // 读取时返回obj1的x属性
|
|||
|
},
|
|||
|
set(value) {
|
|||
|
obj1.x = value; // 设置时修改obj1的x属性
|
|||
|
}
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## 三、核心机制说明
|
|||
|
|
|||
|
1. **代理原理**:
|
|||
|
- 通过 `Object.defineProperty` 在代理对象(obj2)上定义新属性
|
|||
|
- 使用 getter/setter 方法实现对目标对象(obj1)属性的间接访问
|
|||
|
|
|||
|
2. **访问流程**:
|
|||
|
- 读取 `obj2.x` → 触发 getter → 返回 `obj1.x` 的值
|
|||
|
- 设置 `obj2.x` → 触发 setter → 将值赋给 `obj1.x`
|
|||
|
|
|||
|
3. **特性**:
|
|||
|
- 透明的属性访问(使用者无需知道代理存在)
|
|||
|
- 可以在访问前后执行额外逻辑(如验证、日志等)
|
|||
|
|
|||
|
## 四、应用场景
|
|||
|
|
|||
|
| 场景 | 说明 | 示例 |
|
|||
|
| -------- | -------------------- | ------------------------------ |
|
|||
|
| 属性转发 | 跨对象访问属性 | 本示例实现 |
|
|||
|
| 数据验证 | 设置属性前检查有效性 | setter中添加验证逻辑 |
|
|||
|
| 访问控制 | 限制某些属性的访问 | getter中添加权限检查 |
|
|||
|
| 日志记录 | 跟踪属性访问 | getter/setter中添加console.log |
|
|||
|
|
|||
|
## 五、扩展实现
|
|||
|
|
|||
|
### 1. 多属性代理
|
|||
|
```javascript
|
|||
|
function proxyProperties(target, source, props) {
|
|||
|
props.forEach(prop => {
|
|||
|
Object.defineProperty(target, prop, {
|
|||
|
get() {
|
|||
|
return source[prop];
|
|||
|
},
|
|||
|
set(value) {
|
|||
|
source[prop] = value;
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 使用示例
|
|||
|
proxyProperties(obj2, obj1, ['x', 'a', 'b']);
|
|||
|
```
|
|||
|
|
|||
|
### 2. Vue2 风格的数据代理
|
|||
|
```javascript
|
|||
|
function observe(obj) {
|
|||
|
const handler = {
|
|||
|
get(target, prop) {
|
|||
|
console.log(`读取 ${prop}`);
|
|||
|
return target[prop];
|
|||
|
},
|
|||
|
set(target, prop, value) {
|
|||
|
console.log(`设置 ${prop} 为 ${value}`);
|
|||
|
target[prop] = value;
|
|||
|
return true;
|
|||
|
}
|
|||
|
};
|
|||
|
return new Proxy(obj, handler);
|
|||
|
}
|
|||
|
|
|||
|
const observed = observe(obj1);
|
|||
|
```
|
|||
|
|
|||
|
## 六、注意事项
|
|||
|
|
|||
|
1. **性能考虑**:
|
|||
|
- 每个代理属性都会增加访问开销
|
|||
|
- 避免在性能关键路径上过度使用
|
|||
|
|
|||
|
2. **引用关系**:
|
|||
|
- 代理对象和目标对象保持独立
|
|||
|
- 修改代理属性会影响原始对象
|
|||
|
|
|||
|
3. **枚举特性**:
|
|||
|
- 默认情况下代理属性不可枚举
|
|||
|
- 需要显式设置 `enumerable: true`
|
|||
|
|
|||
|
4. **兼容性**:
|
|||
|
- `Object.defineProperty` 是 ES5 特性
|
|||
|
- 现代开发可考虑使用 ES6 Proxy
|