前言
常看到说Vue是响应式的,或者说,它把data中的各种属性通过Object.defineProperty转换成setter/getter,以使得vue能够追踪这些属性的变更,从而适时通知页面重新渲染或者修改Model中的值,从而实现数据的双向传递。
1、初识Object.defineProperty
ES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
语法
Object.defineProperty(obj, prop, descriptor)
参数
-
obj: 要在其上定义属性的对象。
-
prop: 要定义或修改的属性的名称。
-
descriptor: 将被定义或修改的属性的描述符。
如下代码:定义一个person,其中有两个属性,name和address。想要添加一个属性age,可以像前两个属性一样添加,也可以使用Object.defineProperty添加。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Object.defineProperty</title>
</head>
<body>
<script type="text/javascript" >
//定义一个person,给两个属性
let person = {
name: "aaa",
address: "北京"
//age:18
}
//通过defineProperty增加一个属性:age
Object.defineProperty(person,'age',{value:18})
//打印,看看效果
console.log(person)
</script>
</body>
</html>
运行:
这两种方式,初始效果相同,但是,使用Object.defineProperty添加的属性,有一些特殊的约束条件和配置项:
1)使用Object.defineProperty添加的属性,默认情况下,不可被枚举(遍历)
2)使用Object.defineProperty添加的属性,默认情况下,不可被修改
3)使用Object.defineProperty添加的属性,默认情况下,不可以被删除
以上,可以通过相应的配置项来修改其设置:
Object.defineProperty(person,'age',{
value:18,
enumerable:true, //控制属性是否可以被枚举,默认为false
writable:true, //控制属性是否可以被修改,默认为false
configurable:true //控制属性是否可以被删除,默认为false
})
修改后,在console中试试效果(修改前,这几项操作是失败的):
除了以上几个设置外,Object.defineProperty还有一个更重要的功能,setter和getter
2、setter/getter
提一个新的需求,age这个属性的值,希望通过另一个变量获得,而不仅仅通过直接赋值得来,且二者应该能够保持同步(这是实际业务中很常见的需求)。那么,稍稍修改一下代码,定义一个变量,并传值给age:
运行,可以看到,age成功拿到了number中的值,初始化成功。
但是,如果number发生了变化,age却无法跟着同步变化。如下图,我将number变更为19,查看person的age属性,仍然是18。
想要实现二者绑定并同步更新,就需要用到Object.defineProperty的setter和getter了。修改代码如下:
执行,此时观察age的值,并不直接显示,而是一组省略号:
当点击这三个小点时,观察console中打印出getter中的信息,说明此时触发了getter函数,读取并返回了number,即age的值。
此时修改number=19,再次打印person.age,它会自动调用getter,从而获取到最新的number并返回,实现了
同理,试一试setter:
这种number和person.age的数据同步,像不像我们之前所学的v-model实现的双向绑定?
3、Vue中的setter/getter
Vue便是通过Object.defineProperty,将data中的各属性转换成setter/getter,来实现追踪和变更这些属性的值。每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。官网中的图片:
虽然vue的setter和getter提供了很强大的数据同步功能,帮我们处理了view和model间的数据传递问题,但是,官网中有一句很重要的话:Vue不能检测数组和对象的变化。今天就因这句话出了错,回头单独说一说。