数据代理和数据劫持

1.数据代理

  • 含义:数据代理字面意思的意思就是通过间接的方式管理某个数据,例如要访问vue中data的数据应该是vm.data.msg,访问vue中的方法应该是vm.methods.getMsg(),但是实际上我们直接使用vm.msg来访问vm.data.msg,使用vm.getMsg()来访问vm.methods.getMsg(),这就是数据代理
  • 实现原理:通过Object.defineProperty()这个方法,为对象添加一个或多个属性,这个属性自己本身没有值,他的值来源于代理,通过get方法和set方法来管理其代理的数据
  • 基本代码
<script>
function MVVM (option) {
    //保存传入的配置
    this.$option = option
    //保存data对象
    var data = this._data = this.$option.data
    //遍历data中的所有key
    Object.keys(data).forEach(key => {
        //为vm添加与key相同属性,且配置相应的属性描述符
        Object.defineProperty(this,key,{
            configurable: false,//可重新定义
            enumerable: true,//可枚举
            get () {
                //数据代理
                return this._data[key]
            },
            set (newVlue) {
                //数据代理
                this._data[key] = newVlue
            }
        })
    })
}

//实例化
var vm = new MVVM({
    data: {
        msg:'Hello World'
    }
})
</script>

2.数据劫持

  • 含义:数据劫持通俗的说就是监听目标数据的变化,然后触发相应的回调函数,在回调函数中完成设定的逻辑
  • 实现原理:通过Object.defineProperty()这个方法,可以监听目标数据的变化
  • 语法:Object.defineProperty(obj, key,options)
  • 注意点:调用Object.defineProperty()时,需要在闭包环境中进行,因为其get()和set()需要代理一个数据,这个数据已闭包形式存在不会与其他数据冲突
<script>
//封装一个代理方法(闭包环境)
function proxyData(obj,key,val){
    //val作为一个中间变量,负责存储这个属性的值
    Object.defineProperty(obj, key, {
        configurable:false,
        enumerable:true,
        get(){
            //get方法返回这个val
            console.log('通过get方法获取了'+key+'属性的值')
            return val
        },
        set(newVal){
            //set方法修改这个val
            if(newVal === val){
                return
            }
            console.log(key+'属性的值发生了变化')
            console.log('可以在这个回调函数中做其他事件,例如更新页面dom等')
            val = newVal
        }
    })
}

var p = {name: 'kyo', age: 20}
//调用proxyData()进行数据劫持
Object.keys(p).forEach((key) => {
    var val = p[key]
    proxyData(p,key,val)
})

//获取属性值
var name = p.name
//修改属性值
p.age = 27
</script>

控制台打印:

通过get方法获取了name属性的值
age属性的值发生了变化
可以在这个回调函数中做其他事件,例如更新页面dom等

上面的方法只代理了对象的第一层属性,如果对象中包含多层对象,需要对每层次的属性都进行数据劫持的时候,就需要用到递归调用。
思路:对所有的属性进行数据劫持,如果他的属性值类型是object,则对这个属性值进行递归调用

<script>
//利用递归调用深度劫持
function proxyData(obj,key,val){
    //val作为一个中间变量,负责存储这个属性的值
    Object.defineProperty(obj, key, {
        configurable:false,
        enumerable:true,
        get(){
            //get方法返回这个val
            console.log('通过get方法获取了'+key+'属性的值')
            return val
        },
        set(newVal){
            //set方法修改这个val
            if(newVal === val){
                return
            }
            console.log(key+'属性的值发生了变化')
            console.log('可以在这个回调函数中做其他事件,例如更新页面dom等')
            val = newVal
        }
    })
    //判断属性值是否为object,如果是则对这个值进行递归调用
    if(val instanceof Object){
        Object.keys(val).forEach(key => {
            proxyData(val,key,val[key])
        })
    }
}

var data = {
    name: 'kyo',
    age: 20,
    skill: ['大蛇薙', '最终决战奥义“无式'],
    father: {
        name: '草薙柴舟',
        age: 50
    }
}
Object.keys(data).forEach((key) => {
    var val = data[key]
    proxyData(data,key,val)
})

//读取属性值
var fatherName = data.father.name
//修改属性值
data.father.age = 52
</script>

控制台打印:

//data.father.name
通过get方法获取了father属性的值
通过get方法获取了name属性的值

//data.father.age = 52
通过get方法获取了father属性的值
age属性的值发生了变化
可以在这个回调函数中做其他事件,例如更新页面dom等
posted @ 2019-11-13 22:02  ---空白---  阅读(1016)  评论(0编辑  收藏  举报