- Object.defineProperty() 它可以直接对一个现有的对象添加一个属性,或是修改属性
为什么用这个方法呢,因为可以更加详细的进行限制对象的修改
也可以说是用一个对象监听(修改/代理)一个对象的属性
let obj = {
age:1,
name:'xiaoxiao'
}
let num = 9
Object.defineProperty(obj,'age',{
get(){
console.log('查看后被调用了')
return num
},
set(val){
console.log('修改后被调用了,传了这个值:'+val)
num = val
}
})
console.log(obj)
console.log(obj['age'])
- 第一个参数:要修改的对象
第二个参数:修改对象哪个属性
第三个参数:一个对象,这个对象可以配置对第二个参数的具体限制
第三个参数的相关方法
- 第二个参数的值,是get函数的返回值
get没有返回值,第二个参数就为undefined
set.PNG
第三个参数的相关方法
- 修改监听的对象中的属性时,自动调用set方法
-
如果用defineProperty监听一个对象的属性,修改该对象的属性不生效
不生效.PNG
- 要让修改监听对象的属性生效,使用set方法
- set方法:该方法默认有个形参,该形参默认接收属性修改后的值
- 把形参赋值个一个变量
- 在把这个变量让get函数作为返回值
- get 的返回值会赋值给defineProperty监听一个对象的属性(也就是第二个参数)
get(){
//把set里的num在get函数中返回,这个值再赋值给要修改的属性(age)
return num
},
set(val){
//这接收的值val,然后赋值给num
num = val
}
总结:
- defineProperty可以监听一个对象的属性
- set:对象的属性发生改变,第一个形参接收到改变的值
- get: 只要调用对象的属性,该函数自动调用,它的返回值会赋值给监听对象的属性
- 用get可以将set的形参返回给监听对象的属性,这是完美闭环
vue2数据代理
- 什么叫数据代理:一个对象能修改一个对象属性,
比如:B对象能直接修改A对象的属性,B等于{b1:1},A也等于{b1:1},B等于{b1:2},A也等于{b1:1}
这相当于:A=B= {XXX:XXX} - 但是不是把B完完全全赋值给A的,如果完全赋值给A,那我们直接使用B就好了,但我们只使用B对象的一个或多个属性,那么B的某个属性发生变化,是可以defineProperty监听一个B的属性改变
- set:对象的属性发生改变,第一个形参接收到改变的值
- 所以,只要defineProperty监听一个B的某个属性改变,B的某个属性改变,就在set函数把值赋给A对象的属性,那么A对象的属性就能随着B对象的属性的变化而变化,B对象可修改A对象
let A = {'a':1}
//undefined
let B = {'b':2}
//undefined
Object.defineProperty(B,'b',{set(val){A.a = val }})
B.b //现在可以看到,B.b被defineProperty监听了,但是没有get返回值,所以b的值为undefined
//undefined
B.b = 4
//4
A.a
//4
B.b
//undefined
A.a // A.a = val,B的属性值赋值给A.a了,结果为4,即使B.b为undefined,但之前她确确实实接收了B.b
//4
- 为了使B.b的值能生效,添加get函数,并返回
B.b = 4
//4
A.a
//4
B.b
//undefined
A.a // A.a = val,B的属性值赋值给A.a了,结果为4,即使B.b为undefined,但之前她确确实实接收了B.b
//4
Object.defineProperty(B,'b',{get(){return A.a},set(val){A.a = val }})
B.b = 't'
//'t'
B
//{}b: "t"get b: ƒ get()set b: ƒ set(val)[[Prototype]]: Object
B.b
//'t'
A.a
//'t'
添加了get(){return A.a}
- 可以看出来,B.b不再是undefined,只要B.b发生变化,就会改变
- 为什么要添加get(),不添加get(),B.b一直是undefined,这样B.b发生改变无法正确的监测到
比如:B.b已经修改为4了,下次再修改为4的时候,不用在调用set函数,但是,B.b一直是undefined,所以还会再调用set函数,影响效率
使用Object.defineProperty() 定义对象属性时,如已设置 set 或 get, 就不能设置 writable 和 value 中的任何一个了,不然会报如下错误:
Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
at Function.defineProperty (<anonymous>)
- vue2数据代理:
vm实例对象代理的是_data这个对象,把_data这个对象的值,放到vm(Vue实例)这个对象身上,这样使用的时候更加简便
<body>
<div class="root">
<h1>{{_data.name}}</h1> <!-- 不代理,要写_data这个对象才能获取到数据 -->
</div>
<script>
const vm = new Vue({
el:'.root',
data:{
name:'Tom'
}
})
</script>
</body>
- 代理后,简单使用,没有_data
<h1>{{name}}</h1>
- _data是Vue实例的一个对象,将data对象的值赋值给_data
例子:Vue实例的name,代理_data.name
- 当name的值发生改变,调用set()方法,_data.name能随name的变化而变化
- name(改变了)>>>调用set()>>>_data.name(跟着变)
- _data.name(改变了)>>>调用get()>>>name(跟着变)
这时候要注意,是_data.name = data.name,是data.name赋值给_data.name
let _data = {'name':'被监听,他它变,别人跟着变'}
let vm = {'name':'代理了_data.name!'} //随_data.name变化,name = _data.name
Object.defineProperty(vm,'name',{get(){return _data.name},set(val){_data.name = val}})
vm.name='我变了,现在赋值给_data.name' //set(val){_data.name = val}
vm.name
vm.name = 'ooo'
_data.name
数据劫持
响应式是,数据发生改变,页面也发生改变,代理只是