数据劫持

124 阅读3分钟

个人笔记

数据劫持

值得是访问或修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果 经典应用:vue双向绑定原理(vue双向绑定 = 数据劫持 + 发布者订阅模式)

数据劫持实现思路

  • 利用Object.defineProperty 设置set和get
  • 利用ES6新增的proxy设置代理

Object.defineProperty

这个方法接受三个参数

  • 操作对象
  • 操作属性
  • 描述符对象(数据属性/访问器属性)

描述符对象(数据属性)他可以有以下4个属性来进行描述对象属性

  1. configurable表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认为false
  2. enumerable表示能否通过for in循环访问属性,默认为false
  3. writable表示能否修改属性的值,默认为false
  4. value这个属性的数据值,默认为undefinded

栗子

configurable 属性

let p1 = {
    name: '张三',
    age: 18
}
Object.defineProperty(p1,"age",{
    enumerable: false
})
delete p1.age // 无法删除
console.log(p1); //{name: '张三', age: 18} 

enumerable 属性

let p1 = {
    name: '张三',
    age: 18
}
Object.defineProperty(p1,"age",{
    enumerable : false
})
for(let key in p1){
    console.log(p1[key]) // "张三"
}

writable属性

let p1 = {
    name: '张三',
    age : 18
}
Object.defineProperty(p1,"age",{
    writable : false
})
p1.age = 20// 无法删除
console.log(p1)// {name: '张三', age: 18}

value属性

let p1 = {
    name: '张三',
    age : 18
}
Object.defineProperty(p1,"age",{
    value:20
})
console.log(p1)// {name: '张三', age: 20}

描述符对象(访问器属性)

  1. get:在读取属性值的时候调用的函数,默认值是undefined
  2. set:在设置属性值的时候调用的函数,默认值是undefined

栗子

let p1 = {
    name : '张三',
    age : 18
}
// 错误栗子 这两种写法会导致栈溢出
Object.defineProperty(p1,"age",{
    get:function() {
    // 当return p1.age 他会再次调用get 函数,无限循环
        return p1.age
    },
    set:function(value) {
    // 当设置p1.age 他会再次调用set函数,无限循环
        p1.age = value
    }
})
// 正确栗子
let initValue = 20 // 存储初始值
Object.defineProperty(p1,"age",{
    get:function() {
        console.log("获取了age") // 进行一些操作
        return initValue // 返回 initValue
    },
    set:function(value) {
        console.log("设置了age的值") // 进行一些操作
        initValue = value // initValue重新赋值
    }
})
console.log(p1.age) // 获取了age , 20
p1.age = 30
console.log(p1.age) // 设置了age , 30

进阶 处理对象属性值为对象

利用递归 + Object.defineProperty实现

Proxy

Object.defineProperty 的getter 和 setter 是属性级别的拦截,代理是对象级别的拦截!功能更加强大

更多Proxy的解释,方法请移步[(35条消息) 猿创征文|一文带你深入掌握ES6 Proxy数据代理_es6的proxy实现原理_海底烧烤店ai的博客-CSDN博客]

let user = { name: 'Jimmy',age:18 }
    function makeProxy(target) {
        return new Proxy(target, {
            get: (target, key) => {
                console.log('这里获取了这个数据,上报!')
                return target[key]
             },
             set: (target, key, value) => {
                console.log('设置的值是',key,value)
                target[key] = value
            },
    })
}
let proxy =  makeProxy(user)
console.log(proxy)//输出Proxy {name: 'Jimmy', age: 18}
proxy.age = 20 //输出设置的值是 age 20
console.log(proxy.age) //输出这里获取了这个数据,上报! 20

资料来源 b站up主Jimmyhao, csdn[(35条消息) Object.defineProperty方法(详解)_objectdefineproperty_搞前端的小菜的博客-CSDN博客]