vue中数据劫持的底层实现原理
//Vue数据劫持代理
/*
首先,我们要搞清楚,数据劫持,劫持的是什么:data
*/
//模拟Vue里面的data选项
let data={
//dada中是我们初始化数据的地方
username:'zhousujuan',
age:'21'
}
/*
初始化了两个数据:username、age
问题:
谁来劫持它们?(这两个初始化的数据)
this
*/
//模拟组件的实例
//实例上原本没有这个初始化的数据
//现在我们要去劫持代理上面的属性
//也就是说,我们最终的目标是;
//在this上面有这些属性
// let _this={
// username:'zhousujuan',
// age:'21'
// }
//但是底层上面的实现,不可能是直接粘贴复制---》而是要通过编码的形式去实现的
// 我们怎么去做尼?
/**
* 1.可能有些认为,可以直接:_this.username=data.username;
* 这不叫劫持代理,这叫做赋值
* 而且,这样直接写的话,存在一个问题:
* 我们修改_this.username这里的属性的时候,并不能监视到这个属性的变化
* 所以并不是这个方法
*
* 2.利用Object.defineProperty
* 如何去实现尼?
* 首先对data中的所有属性进行枚举,
* 只有枚举出来,才能得到里面的key和对应的value
* 进而才能想办法代理到我们的实例身上
*/
//这里因为this是一个关键字,所以我们这里用_this来展示
let _this={
username:'zhousujuan',
age:'21'
}
//错误方法
// _this.username=data.username;
//利用Object.defineProperty
//这个时候我们可以利用一个for循环
for(let item in data){
//打印item和里面的value
// console.log(item,data[item])
/**
* 查看后台打印,可以得到相应的值
* 拿到之后,把里面的属性设置给this
* 利用Object.defineProperty
* 参数1:设置的目标对象:_this
* 参数2:添加的属性(key):item
* 参数3:对应的value应该是一个说明对象
*
*/
Object.defineProperty(_this,item,{
//这里面有很多种写法
/**
* 比如说,我们可以通过value去设置,但是这种没有get和set
* 所以我们对应,应该用get和set写
*/
get(){//用来获取扩展属性值的,当获取该属性值的时候调用get方法(也就是点击那三个点的时候)
//get方法一定有一个返回值,这个返回值,其实就是当前扩展属性的value--->data[item]
return data[item]
},
set(newValue){
//监视扩展属性的,只要已修改就调用
console.log('set()',newValue);
//a打印出来了,说明set调用了,因为下面在进行修改
//而且调用的时候,我们可以拿到最新的value值
//既然外面不能进行修改,那么我们里面可不?
// _this.username=newValue;
/**
* 这是一个死循环,
* 为什么?
* 因为set是一修改就会进行调用,
* 首先,我们的外面进行修改--》调用set,
* set里面又进行修改,又进行了调用,
* 所以就进入了死循环
* 并且我们没有结束的条件
* ===》千万不要在我们的set方法中直接修改我们的扩展属性的值,会出现死循环
*
* 在这里直接修改,有什莫用?
* 在这里,它扮演的是监视
* 我们想要修改_this身上的值,直接修改,修改不了,我们可以采取迂回战术
* 注意:
* _this身上对应的key叫什么,取决于谁?
* 我们这里是把data上面的值,劫持到_this身上,所以我们可以通过修改data里面的值,
* 修改:1.找到data对象
*/
//1.找到data对象,注意我们要修改谁,当前的item
//因为这是一个变量,对象操作变量的时候,我们需要需要中括号
//当我们这样的话,整体的思路是:
/**
* 当我们修改_this的value的时候,调用set
* 拿到我们最新的value赋值给data
*把data中对应的属性进行修改
因为data中的已修改
又里面的扩展属性是依赖于人家的
所以,_this里面的会跟着变化
*/
data[item]=newValue;
}
})
}
//get写完之后,我们在进行一下打印
console.log(_this);
/**
* 现在这个对象,不展开,什么属性都没有
* 而我们展开看(因为扩展属性,需要我们展开之后,才能看得到)
* 可以看到age、usrname
* 而且我们看这两个属性,并不能直接看到它们的value
* 显示的是age(...)
* 我们需要点击一下,才能看到---》这个是典型的通过get得到的value值
* 如果_this这个对象里面有个test:'测试',这样打印出来的就没有三个.,而是直接显示出来
*/
//除了get还有set
/**
* 但是我们这里先进行,通过_this修改里面的属性
* 不能修改成功,还是原来的值
* 通过Object.defineProperty的get方法添加的扩展属性不能直接对象.属性,进行修改
* 虽然不能直接修改,但是会触发set()方法
*/
_this.username='liliy';
//打印里面的username
console.log(_this.username);
//不能修改成功,还是原来的值
//通过Object.defineProperty的get方法添加的扩展属性不能直接对象.属性,进行修改