vue----数据代理机制
Object.defineProperty()
- 这个方法是ES5新增加的。
- 这个方法是数据代理机制的基础。
- 这个方法的作用:给对象新增属性,或者设置对象原有的属性。
- 怎么用?
通过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为:
数据描述符 --- 特有的两个属性(value、writable)
存取描述符 --- 由一对getter、setter 函数功能来描述的属性。
Object.defineProperty(给那个对象新增属性, '新增的这个属性名叫啥', {给新增的属性设置相关的配置项key:value对})
第三个参数是属性相关的配置项,配置项都有那些?每个配置项的作用是啥?
value 配置项:给属性指定值
writable 配置项:设置该属性的值是否可以被修改。true表示可以修改。false(默认值)表示不可以修改。
getter方法 配置项:不需要我们手动调用。当读取属性值的时候,getter方法被自动调用。
getter方法的返回值非常重要,这个返回值就代表这个属性它的值。
setter方法 配置项:不需要我们手动调用。当修改属性值的时候,setter方法被自动调用。
setter方法上有一个参数,这个参数可以接受传过来的值。
注意:当配置项当中有setter 和 getter 的时候,value 和 writable 配置项都不能存在。
enumerable 配置项:设置该属性是否可以被迭代(遍历、枚举)。true表示可以遍历。false(默认值)表示不可遍历。
configurable 配置项:设置该属性是否可以被删除。true表示可以被删除。false(默认值)表示不可以被删除。
Object.defineProperty()方法----代码示例
<script>
let obj = {}, obj2 = {}, obj3 = {}
let temp //使用全局变量
//数据描述的方式
Object.defineProperty(obj, 'name', {
value: 'GDB',
writable: true
})
//存取描述的方式
Object.defineProperty(obj2, 'name', {
get: function(){
console.log("getter方法执行了 ")
return temp
},
set: function(value){
console.log("setter方法执行了 ")
temp = value
}
})
//如果存取描述的方式不适用全局变量的方式会出现递归调用最后堆栈溢出
Object.defineProperty(obj3, 'name', {
get: function(){
console.log("getter方法执行了 ")
return this.name
},
set: function(value){
console.log("setter方法执行了 ")
this.name = value
}
})
</script>
Vue数据代理
- 什么是数据代理机制?
通过访问 代理对象的属性 来间接访问 目标对象的属性。
数据代理机制的实现需要依靠:Object.defineProperty()方法。 - ES6新特性:
在对象中的函数/方法::function 是可以省略的。
<div id="app">
<h1>{{msg}}</h1>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg: 'GDB'
}
})
let target = {
name: 'GDB'
}
let proxy = {}
//注意:代理对象新增的这个属性的名字和目标对象的属性名要一致。
Object.defineProperty(proxy, 'name', {
get(){
console.log('getter 方法执行了 ')
return target.name
},
set(value){
console.log('getter 方法执行了 ')
target.name = value
}
})
</script>
Vue数据代理机制对属性名的要求
- Vue 实例不会给以 _ 和 $ 开始的属性名做数据代理。
如果运行给 _ 和 $ 开始的属性名做数据代理的话,vm这个Vue实例上可能出现_xxx 或 $xxx属性,而这个属性名可能会和Vue框架自身的属性名冲突。 - 在Vue当中,给data对象的属性名命名的时候,不能以_或$开始。
Vue框架数据代理部分源码解读
Vue 框架源代码中关键性代码:
1. var data = vm.$options.data;
注意:这是获取 data。程序执行到这里的时候 vm 上还没有 _data属性。
2. data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
程序执行完这个代码之后,vm 对象上多了一个_data这样的属性。
通过以上源码解读,可以得知 data 不一定是一个 {},也可以是一个函数。
代码含义:
如果 data 是函数,则调用 getData(data, vm)来获取data。
注意:如果是函数的话,必须使用 return 语句返回 {} 对象。
如果 data 不是函数,则直接将data返回给 data变量。并且同时将data赋值给vm._data属性了。
程序执行到这里,为什么要给 vm 扩展一个_data属性呢?
_data属性,以"_"开始,足以说明,这个属性Vue框架底层需要访问的。
Vue框架使用vm._data这个属性干啥?
vm._data 就是传进去的数据对象 data
vm._data 这个属性直接指向了底层真实的data对象。通过_data访问的属性是不会走数据代理机制的。即不会调用getter和setter方法。
注意:对于Vue实例vm来说,不仅有_data这个属性,还有一个$data属性。
_data 是框架内部使用的,可以看作私有的。
$data 这个Vue框架对外公开的一个属性,是给我们程序员使用的。
3. 重点函数:
function isReserved(str) {
var c = (str + '').charCodeAt(0);
return c === 0x24 || c === 0x5f;
}
这个函数是用来判断字符串是否是以 _ 和 $ 开始的。
4. proxy(vm, "_data", key);
通过这行代码进入代理机制(数据代理)
5. 重点函数 proxy() ======> target=vm, sourceKey="_data", key='需要增加的属性名'
function proxy(target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key];
};
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val;
};
Object.defineProperty(target, key, sharedPropertyDefinition);
}
- 我个人觉得Vue实例对象vm就是proxy对象,传进去的data对象就是target对象,而_data和$data是指向data的两个指针而已。
参考
其他参考文章以及文章部分内容的来源
参考:Object.definProperty()方法讲解
来源:老杜的视频讲解