要想理解数据代理就需要对object.defineProperty这个方法特别熟。这个方法在object身上,名字叫做defineProperty,这个方法可不要小瞧,vue底层很多地方都使用到了。
比如vue里面使用的数据劫持就使用到了这个方法,数据代理用到了这个方法,后面计算属性也使用到了这个方法。
给对象添加属性
这个defineProperty就是给一个对象添加属性使用的,或者说给一个对象定义属性用的。
这里传入三个参数,第一个是给哪个对象添加属性,第二个添加的属性叫做什么名字,最后就是一个特别重要的参数叫做配置项,配置项里面可以写很多的配置。
其实你在person对象里面直接添加age属性就行了,但是这种方式没有defineProperty方法高级。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<script type="text/javascript">
//定义对象
let person = {
name: "zhangsan",
sex: "male"
}
//现在想要张山有年龄,添加一个属性age 18
Object.defineProperty(person,'age',{
value: 18 //这样person对象就有了age这个属性,并且是18
})
console.log(person)
//添加了这个属性,但是不参与遍历,也就是不可以枚举
//这个方法可以将传入对象里面的所有属性名提取出来变成数组
console.log(Object.keys(person))
//for i in既可以遍历数组,也可以遍历对象
for(i in person){
console.log(i)
}
</script>
</body>
</html>
enumerable: true? 控制属性是否可以枚举,默认值是false
如果属性可以遍历那就需要借助另外一个配置项了,?enumerable,这样虽然可以遍历了,但是需要修改该属性值还是修改不了的。?
writable: true? 可以修改属性值? ?configurable: true??可以删除属性值
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<script type="text/javascript">
//定义对象
let person = {
name: "zhangsan",
sex: "male"
}
//现在想要张山有年龄,添加一个属性age 18
Object.defineProperty(person,'age',{
value: 18, //这样person对象就有了age这个属性,并且是18
enumerable: true, //控制属性是否可以被枚举,默认值是false
writable: true, //控制属性是否可以被修改,默认值是false
configurable: true //控制属性是否可以被删除,默认值false
})
person.age = 19
console.log(person.age)
console.log(delete person.age)
console.log(Object.keys(person))
for(i in person){
console.log(i)
}
</script>
</body>
</html>
上面方法和四个配置项看起来复杂,但是可以对追加的配置项的属性进行很多高级的限制,比如能不能修改,能不能删除,能不能枚举。
---------------------------------------------------------------------------------------------------------------------------------
上面都是基本的配置项,其实还可以传高级的配置项。
现在需要让person对象有一个age属性,age属性保存年龄,并且它的数值是读取num变量读取出来的,而不是自己定义的,并且这个age的值的变化跟着num变量一起变化。
这里就需要借助defineProperty,这里面能够写一个配置,配置的名称叫做get,值是一个函数。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<script type="text/javascript">
let num = 19
//定义对象
let person = {
name: "zhangsan",
sex: "male"
}
//现在想要张山有年龄,添加一个属性age 18
Object.defineProperty(person,'age',{
// value: 18, //这样person对象就有了age这个属性,并且是18
// enumerable: true, //控制属性是否可以被枚举,默认值是false
// writable: true, //控制属性是否可以被修改,默认值是false
//configurable: true //控制属性是否可以被删除,默认值false
//当有人读取person的age属性时候,get函数(getter)就会被调用,返回值就是age的值
get(){
console.log("有人读取了age属性")
return num
},
//当有人修改person的age属性时候,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log("有人修改了age属性且值是",value)
num = value
}
})
person.age = 19
console.log(person.age)
console.log(delete person.age)
console.log(Object.keys(person))
for(i in person){
console.log(i)
}
</script>
</body>
</html>
有没有一种感觉,person和num其实是两个东西,借助defineProperty让其产生关联。得有这种体会,person确实是一个对象,确实有age属性,但是值现用,我们也现取,取靠get,要改就调用set,并且将值给你拿过来。(这个get和set是非常重要的)
?
?
通过一个对象代理另外一个对象中属性的操作(读写)。
有个对象obj,它有一个属性是x,想访问就obj.x,或者obj.x=xxx直接赋值就行。
现在有obj2,希望让obj2也能够访问到obj.x,也希望obj2以后能够修改这个x,这就是通过一个对象obj2,代理另外一个对象obj中属性的操作。
下面就是最简单的数据代理:obj1可以操作自身的x,但是obj2可以通过数据代理操作obj1的x,这就高级了,这就是数据代理。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<script type="text/javascript">
//定义对象
let obj1 = {
x: 100
}
let obj2 = {
y: 200
}
//想通过obj2能够读取到x并且可以进行修改
//操作对象为obj2,追加一个属性叫做x
Object.defineProperty(obj2,'x',{
//以后有人试图读取obj2.x属性值的时候,给它的是obj1.x的属性值
get(){
alert("读取obj2的x属性了")
return obj1.x
},
//有人想更改obj2的x属性值,那么就更改obj1.x的值即可
set(value){
alert("修改obj2的x属性了")
obj1.x = value
console.log("修改之后obj2.x值为:" + obj2.x)
}
})
</script>
</body>
</html>