Toc
  1. 一、数据劫持
  2. 二、Proxy数据代理
Toc
0 results found
bbcfive
关于数据劫持与数据代理

本文转自:《Js数据劫持与数据代理》

一、数据劫持

主要使用Object.defineProperty()实现

1
2
3
4
5
6
7
8
9
let obj = {}, val = ''
Object.defineProperty(obj, 'val', {
get() {
return val
},
set(newVal) {
val = newVal
}
})

Object.defineProperty()的缺点:

  1. 无法监听到数组变化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //当被监听的属性是数组时
    let arr = [1,2,3]
    let obj = {}
    Object.defineProperty(obj,'arr',{
    get() {
    return arr
    },
    set(newVal) {
    arr = newVal
    }
    })
    obj.arr.push(4) // 执行了get 实际上是改变arr的值但是却没有执行set而是执行了get
    obj.arr = [1,2,3,4] // 执行了set
    当被监听的属性是数组,这几个方法push、pop、shift、unshift、splice、sort、reverse不会触发set。vue将这几个修改原始的数组的方法称为变异方法
  2. 必须遍历对象的每一个属性
    1
    2
    3
    4
    5
    Object.keys(obj).forEach(key=>{
    Object.defineProperty(obj,key,{
    //....
    })
    })
  3. 必须深层遍历嵌套对象

当遇到变异方法时旧版本的vue通过重写方法来进行数据劫持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
const arrayAugmentations = [];
aryMethods.forEach((method)=> {
// 这里是原生 Array 的原型方法
let original = Array.prototype[method];
// 将 push, pop 等封装好的方法定义在对象 arrayAugmentations 的属性上
// 注意:是实例属性而非原型属性
arrayAugmentations[method] = function () {
console.log('has change');
// 调用对应的原生方法并返回结果
return original.apply(this, arguments);
};
});
let list = ['a', 'b', 'c'];
// 将我们要监听的数组的原型指针指向上面定义的空数组对象
// 这样就能在调用 push, pop 这些方法时走进我们刚定义的方法,多了一句 console.log
list.__proto__ = arrayAugmentations;
list.push('d'); // 我被改变啦!
// 这个 list2 是个普通的数组,所以调用 push 不会走到我们的方法里面。
let list2 = ['a', 'b', 'c'];
list2.push('d'); // 不输出内容

二、Proxy数据代理

劫持监听对象所发生的相应改变,并代理其做出相应改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var arr = [1,2,3]
var handle = {
//target目标对象 key属性名 receiver实际接受的对象
get(target,key,receiver) {
console.log(`get ${key}`)
// Reflect相当于映射到目标对象上
return Reflect.get(target,key,receiver)
},
set(target,key,value,receiver) {
console.log(`set ${key}`)
return Reflect.set(target,key,value,receiver)
}
}
//arr要拦截的对象,handle定义拦截行为
var proxy = new Proxy(arr,handle)
proxy.push(4)

proxy的优点:

  1. 使用proxy可以解决defineProperty不能监听数组的问题,避免重写数组方法;
  2. 不需要再遍历key。
  3. Proxy handle的拦截处理器除了get、set外还支持多种拦截方式,具体请查阅官方文档(https://developer.mozilla.org...)
  4. 嵌套查询。实际上proxy get()也是不支持嵌套查询的。解决方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    let handler = {
    get (target, key, receiver) {
    // 递归创建并返回
    if (typeof target[key] === 'object' && target[key] !== null) {
    return new Proxy(target[key], handler)
    }
    return Reflect.get(target, key, receiver)
    }
    }
本文作者:bbcfive
版权声明:本文首发于bbcfive的博客,转载请注明出处!