数据劫持

数据劫持

Jul 27, 2022 ·
5 Min Read

使用过 Vue 框架的应该就不陌生了,因为 Vue 中的响应式原理就是 数据劫持 和 订阅发布模式。而这里主要讲的是 数据劫持

数据劫持:在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。最典型的使用场景就是响应式。

  1. 在 Vue 2.x 利用 Object.defineProperty()。(具体想知道 vue 中怎么实现的可以百度一下,网上都一大把)
  2. 在 Vue 3.x 版本之后改用 Proxy 进行实现。

基础

首先说一下两个语法:

Object.defineProperty(obj, prop, descriptor)

该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

详情 → 传送门

小DEMO

const data = {}
let a = 2;
Object.defineProperty(data, 'a', {
enumerable: true,
configurable: false,
get: () => {
console.log('get:', a)
return a
},
set: (newValue) => {
console.log('set:', newValue)
a = newValue
}
})
data.a = 10
console.log(data.a)
// 输出:
// set: 10
// get: { a: 10 }
// 10

new Proxy(target, handler)

Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

详情 → 传送门

小DEMO

const handle = {
get(obj, prop, value) {
console.log('get:', value)
return value
},
set(obj, prop, value) {
obj[prop] = value
console.log('set:', value)
return true
}
}
const data = {
a: 1
}
const proxy = new Proxy(data, handle);
proxy.a = 10
console.log(proxy.a)
// 输出:
// set: 10
// get: { a: 10 }
// 10

Object.defineProperty 对比 Proxy

Object.defineProperty的问题有三个:

Proxy优点:

缺点:

利用 Proxy 实现类似 Vue 中的数据挟持

class Observe {
constructor(data) {
this.handle = {
set: (target, property, value) => {
console.log('set:', value)
// 判断新值是否等于旧值
if (target[property] === value) {
return false;
}
target[property] = value;
// TODO 数据变化,通知所有订阅者
return true;
},
get: (target, property) => {
console.log('get:', target[property])
return target[property];
}
}
// 监听每一个数据
this.data = this.defineReactive(data);
}
// 利用proxy监听data,循环遍历监听data下所有对象,并返回proxy
// 注:因为proxy与Object.defineProperty不同,可以监听一个对象,但一个对象的深层对象会监听不到,所有需要遍历监听所有引用类型的。
defineReactive(data) {
if (!data || typeof data !== "object") {
return data;
}
Object.keys(data).forEach(key => {
data[key] = this.defineReactive(data[key]);
});
return new Proxy(data, this.handle);
}
}
const data = {
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
}
const proxy = new Observe(data).data
console.log(proxy.b.d.e)
proxy.b.d.e = 10
console.log(proxy.b.d.e)
// 输出:
// 第一步操作 console.log(proxy.b.d.e)
// get: { c: 2, d: { e: 3 } }
// get: { e: 3 }
// get: 3
// 3
// 第二步操作 proxy.b.d.e = 10
// get: { c: 2, d: { e: 3 } }
// get: { e: 3 }
// set: 10
// 第三步操作 console.log(proxy.b.d.e)
// get: { c: 2, d: { e: 10 } }
// get: { e: 10 }
// get: 10
// 10
Last edited Feb 15