数据代理和数据劫持
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等