浅识js——数据劫持

456 阅读1分钟

js基础知识——数据劫持

一、数据劫持

  • 将来我们在使用框架的时候(vue), 框架目前都支持一个 "数据驱动视图",完成数据驱动视图 需要借助 数据劫持帮助我们完成
  • 以原始数据为基础, 对数据进行一份复刻, 复刻出来数据是不允许修改的, 值从原始数据里面获取
  • 语法:Object.defineProperty('哪一个对象', '属性', '配置项')
  • 配置项:
    • value: 这个属性对应的值
    • writable: 该属性是否可以被重写, 默认是 false 不允许被修改
    • enumerable: 该属性是否可以被枚举, 默认是 false 不能被枚举到
    • get: 是一个函数, 叫做 getter 获取器, 可以决定当前属性的值, 不能与 value writable 同时出现
    • set: 是一个函数, 叫做 setter 设置器, 当你需要修改这个属性的时候, 会触发该函数
      const obj = {}
      obj.name = '哈哈'

      Object.defineProperty(obj,'age',{
        // value: 18,
        // writable: true,
        enumerable: true,
        get () {
          
          console.log('访问age属性,这个函数内可以做很多事')
          return 300
        },
        set (val) {
          console.log('修改obj的age属性,触发setter,想要设置的值为:',val)

        }
      }) 
      obj.age = 99
      console.log(obj)

二、数据劫持与渲染页面

      const box = document.querySelector('.box')
      const box2 = document.querySelector('.box2')
      // 原始对象
      const obj = {}
      obj.name = '哈哈'
      obj.age = 20

      const res = {}
      Object.defineProperty(res,'age', {
        enumerable: true,
        get () {
          
          // console.log('访问age属性,这个函数内可以做很多事')
          return obj.age
        },
        set (val) {
          // console.log('修改obj的age属性,触发setter,想要设置的值为:',val)
          box.innerHTML = `res年龄:${val}`  //改变res.age的值
          obj.age = val   
        }
      }) 
      res.age = 555
      box.innerHTML = `res年龄:${res.age}`
      box2.innerHTML = `obj年龄:${obj.age}`
      obj.age = 6  //虽然值被改变,但不会在页面中显示出来
      res.age = 2  //可以改变age的值

三、封装数据劫持

    const box = document.querySelector('.box')
    const box2 = document.querySelector('.box2')

    // 原始对象
    const obj = {}
    obj.name = '哈哈'
    obj.age = 20
    // 如果劫持的属性多了,原本的写法不方便,代码量较多,所以 封装数据劫持
    
    function observer (origin,callback) {
      //1.创建一个对象,将origin内部的属性劫持到这个对象内
      const target = {}

      // 2. 劫持origin上的属性到target 中
      for(let key in origin) {
        Object.defineProperty(target,key, {
          enumerable: true,
          get () {
            return origin[key]
          },
          set (val) {
            // console.log('修改obj的age属性,触发setter,想要设置的值为:',val)
            origin[key] = val   
            callback(target)
          }
        }) 
      }
      // 3.将劫持后的target 返回出去
      return target
    }

    const newObj = observer(obj,fn)

    function fn (res) {
      box.innerHTML = `年龄:${res.age} ;名字:${res.name}:`
    }
   
    newObj.age = 2
    newObj.name = '呵呵'

四、数据劫持升级版

  • 升级版数据劫持语法: Object.defineProperties('那个对象', '配置项')
    //原始对象
    const obj = {}
    obj.age = 18
    obj.name = '张三'
    
    升级的基础版-----将数据劫持后的对象属性放在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
        }
      }
    })

//利用循环 优化代码量  
      const res = {}
      for (let key in obj) {
        Object.defineProperties(res,{
          // 此处的key我们需要当一个变量使用,如果直接写 会当成一个字符串,解决方法:在key加一个[]包裹起来,当成变量
          [key]: {
            get () {
              return obj.[key]
            },
            set (val) {
            obj[key] = val
            box2.innerHTML = `res对象age属性:${val},name属性${res.name}`
            }
          },
        })
      }
    // 首次打开页面,给页面做一个赋值
    box1.innerHTML = `obj对象age属性:${obj.age},name属性${obj.name}`
    box2.innerHTML = `res对象age属性:${res.age},name属性${res.name}`
    // 首次渲染完毕页面后, 更改两个页面的属性
    obj.age = 55
    obj.name = 'liu' //obj的修改不会影响视图

    res.age = 666   
    res.name = '王五' //res修改会触发set函数,set函数内有一行代码会让box2更新,所以修改会重新渲染页面
  • 上述方法的晋级版
    // 升升级版!!
    for (let key in obj) {
    Object.defineProperties(obj,{
      // 通常'自己劫持自己'时,不会在对象的原操作属性上操作,而是复制出一份一模一样的数据 操作
      // 为和原属姓名相同,所以会在原属姓名前加一个下划线区分
      ['_' + key]:{
        value:obj[key],
        writable:true
      },
      [key]: {
        get () {
          return obj['_' + key]
        },
        set (val) {
          obj['_' + key] = val
          box1.innerHTML = `obj对象age属性:${obj.age},name属性${obj.name}`
        }
      }
    })
  }
  box1.innerHTML = `obj对象age属性:${obj.age},name属性${obj.name}`
  // 首次渲染完毕页面后 更改对象的属性值
  obj.age = 111
  obj.name = 'pp'