JS中关于数据劫持

91 阅读2分钟

数据劫持

  • 原因:

    • 将来我们使用框架的时候(vue),框架目前都支持一个 "数据驱动视图"
    • 完成数据驱动视图 需要借助 数据劫持 帮助我们完成
  • 定义

    • 以原始数据为基础,对数据进行一分复刻
    • 复制出来的数据是不允许修改的,值从原始数据里面获取
  • 语法

    • Object.defineProperty('哪一个对象','属性','配置项')
    • 配置项:
      • value: 这个属性对应的值
      • writable: 该属性是否可以被重写,默认是 false 不允许被修改
      • enumerable: 该属性是否可以被枚举,默认是 false 不能被枚举到
      • get: 是一个函数,叫做 getter 获取器,可以决定当前属性的值,不能与 value 与 writable 同时出现
      • set: 是一个函数,叫做 setter 设置器,当你需要修改当前属性的时候,会触发该函数
  • 下面是完整的数据劫持代码

<div class="box"></div>
<div class="box2"></div>

//获取元素
const box = document.querySelector('.box')
const box2 = document.querySelector('.box2')

//原始对象
const obj = {}
obj.name = '张三'
obj.age = 18

//将 obj 的属性,劫持到这个对象内
const res = {}

Object.defineProperty(res,'age',{
    enumerable:true,
    get () {
        return obj.age
    },
    set (val) {
        box.innerHTML = `res年龄: ${val}`
        obj.age = val
    }
})
res.age = 999
console.log(res)
box2.innerHTML = `obj年龄:${obj.age}`

封装数据劫持

<div class="box"></div>
<div class="box2"></div>
<script>
    const box = document.querySelector('.box')
    const box2 = document.querySelector('.box2')
    
    //原始对象
    const obj = {}
    obj.name = '张三'
    obj.age = 18
    
    //封装数据劫持
    function observer(origin,callback) {
        //1.创建一个对象,将 origin 内部的属性劫持到了这个对象内
        const target = {}
        
        //2.劫持 origin 上的属性到 target 中
        for(let key in origin){
            Object.defineProperty(target,key,{
                enumerable: reue,
                get () {
                    return origin[key]
                },
                set (val) {
                    origin[key] = val
                    callback(target)
                }
            })
        }
        //99将劫持后的 target 返回出去
        return target
    }
    
    const newObj = observer(obj,fn)
    
    function fn(res){
        box.innerHTML = `年龄:${res.age}; 名字:${res.name}`
    }
    
    newObj.age = 999
    newObj.name = 'gg'
    
</script>

数据劫持升级

  • 语法: Object.defineProperties('哪个对象','配置项')
<h1 class="box1"></h1>
<h1 class="box2"></h1>
<script>
    //获取元素
    const box1 = document.querySelector('.box1')
    const box2 = document.querySelector('.box2')
    
    //原始对象
    const obj = {}
    obj.name = '张三'
    obj.age = 18
    
    //将数据劫持后的对象属性存放在 res 对象中
    const res = {}
    //基础版
    // Object.defineProperties(res,{
    //   age:{
    //     get () {
    //       return obj.age
    //     },
    //     set (val) {
    //       box2.innerHTML = `res 对象的age属性: ${val},name 属性: ${res.name}`
    //       obj.age = val
    //     }
    //   },
    //   name: {
    //     get () {
    //       return obj.name
    //     },
    //     set (val) {
    //       box2.innerHTML = `res 对象的age属性: ${res.age},name 属性: ${val}`
    //       obj.name = val
    //     }
    //   }
    // })
    // console.log(res)
    
    //利用循环优化代码量
    for(let key in obj) {
        Object.defineProperties(res,{
            [key]:{
                get (){
                    return obj[key]
                },
                set (val) {
                    obj[key] = val
                    box2.innerHTML = `res 对象的 age 属性: ${res.age},name 属性: ${res.name}`
                }
            }
        })
    }
    
    //首次打开页面,给页面做一个赋值
    box1.innerHTML = `obj 对象的age属性: ${obj.age},name 属性: ${obj.name}`
    box2.innerHTML = `res 对象的age属性: ${res.age},name 属性: ${res.name}`
    
    //首次渲染完毕页面后 更改两个对象的属性值
    obj.age = 666
    obj.name = '李四'

    res.age = 999
    res.name = '锅锅'
    
</script>

数据劫持升级(自己劫持自己)

//获取元素he原始对象还用上面的
for (let key in obj){
    Object.defineProperties(obj,{
        /**
         * 通常我们在处理"自己劫持自己"的时候,不会再对象的原属性上操作,而是复制出来一份一模一样的数据操作
         * 
         * 为了和原属性名相同,所以会在原本的属性名前加一个 下划线,用来区分
        */
        ['_' + key]: {
            value:obj[key],
            writable:true
        },
        [key]: {
            get() {
                return obj['_' + key]
            },
            set(val) {
                obj['_' + key] = val
                box.innetHTML = `obj 对象的 age 属性: ${obj.age},name 属性: ${obj.name}`
            }
        }
    })
}

//首次渲染完毕页面后 更改两个对象的属性值
obj.age = 666
obj.name = '李四'

res.age = 999
res.name = '锅锅'

数据代理

  • 是官方给的名字,有部分程序员还是习惯性地叫做 数据劫持
  • proxy 是 ES6以后官方推出的 是一个内置构造函数
const obj = {
    name: '张三',
    age: 18
}

//new Proxy 第一个参数:要代理的对象,  第二个参数:一些配置项,  最后会返回一个代理后的对象,我们需要一个变量去接收
const res = new Proxy(obj,{
    get (target,property){
        /**
         * 第一个形参target: 就是你要代理的这个对象,在当前案例中指的就是 obj
         * 第二个形参property:就是该对象内部的某一个属性,自动分配
        */
        return target[property]
    },
    set(target,property,val){
        target[property] = val
        console.log(`你现在想要修改 形参 target 的属性${property},修改的值为 ${val},除此之外你还可以做很多事`)
    }
})

//在代理完成后,给原始对象新加一个属性,此时代理对象依然能够访问到 (Proxy 独有的功能)
obj.abc = 'qwer'

res.age = 66
res.name = '锅锅'