1 Object.defineProperty方法
在Vue中,有很多地方使用到了Object.defineProperty 方法,例如数据劫持、数据代理、计算属性等。Object.defineProperty 方法用来给一个对象添加属性,或者修改一个对象上的属性。它的语法如下:
Object.defineProperty(obj, prop, descriptor)
在默认情况下,使用Object.defineProperty 添加的属性不可以执行枚举、修改等操作,因此需要在descriptor 中添加响应的配置项,配置项具体如下:
配置项 | 描述 |
---|
value | 该属性对应的值,可以是任何有效的JavaScript 值(数值,对象,函数等),默认为undefined | enumerable | 表示该属性是否可以枚举,true为可枚举,默认为false不可枚举 | writable | 控制属性是否可以被修改,true为可以修改,默认为false | configurable | 控制属性是否可以被删除,true为可以被删除,默认为false | get(){} | 属性的getter函数,当访问该属性时,会调用此函数,该函数的返回值会被用作属性的值 | set(){} | 属性的setter函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),默认为undefined |
示例:使用Object.defineProperty() 方法为person 对象添加一个新属性
let person = {};
Object.defineProperty(person, "name", {
value: "橘猫吃不胖",
enumerable: true,
})
console.log(person)
person.name = "张三";
console.log(person)
delete person.name;
console.log(person)
由上面代码可知,无法修改和删除name 属性,因此为其再添加配置项writable (是否可以被修改)和configurable (是否可以被删除),就可以执行修改和删除的操作了,代码如下:
let person = {};
Object.defineProperty(person, "name", {
value: "橘猫吃不胖",
enumerable: true,
writable: true,
configurable: true,
})
console.log(person);
person.name = "张三";
console.log(person);
delete person.name;
console.log(person);
示例:自定义setter和getter
let person = {};
let str = "橘猫吃不胖";
Object.defineProperty(person, "name", {
get() {
return str;
},
set(value) {
str = value;
}
})
console.log(person.name);
person.name = "张三";
console.log(person.name);
2 什么是数据代理
数据代理就是通过一个对象代理对另一个对象中属性的操作(读/写)。
比如说,有两个对象obj1和obj2,其中obj1中有一个属性x,但是在obj2中可以读取和修改obj1中的属性x,这样就达到了一个数据代理的效果,代码如下:
let obj1 = { x: 100 };
let obj2 = { y: 200 };
Object.defineProperty(obj2, "x", {
get() {
return obj1.x;
},
set(value) {
obj1.x = value;
}
})
以上代码就是最简单的一个数据代理,我们可以通过obj2访问并修改obj1中的x的值,示例如下:
console.log(obj2.x);
console.log(obj1.x);
obj2.x = 150;
console.log(obj2.x);
console.log(obj1.x);
3 Vue中的数据代理
下面一段代码中,我们在data 中定义了name 和age ,然后在页面上使用双大括号{{}} 进行了数据显示。其中,Vue实例vm 的身上会出现name 和age 两个属性。
<div id="app">
<h3>姓名:{{name}}</h3>
<h3>年龄:{{age}}</h3>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
name: "橘猫吃不胖",
age: "2岁"
}
})
</script>
我们在控制台输出一下vm 实例,可以看到name 和age 两个属性,当我们将鼠标悬停在(...) 上时,显示了Invoke property getter 。通过对Object.defineProperty() 方法的了解,我们可以知道name 和age 两个属性都是通过Object.defineProperty() 方法添加上来的。 当访问name 时,是getter在工作,当修改name 时,是setter在工作,因此,在vm 中,我们也可以看到它们的getter和setter方法。 那么我们通过vm 读取与修改name 都是通过读取与修改data 中的name ,这就是一个数据代理。
接下来可以对我们刚才的结论进行验证。
验证getter方法
验证思路如下:当我们修改data 中的name 时,vm 中的name 也会进行一个修改。首先在控制台读取当前的name 值vm.name ,我们通过vm 来获得name ,那么getter会将data 中的name 返回给vm 。 那么接下来我们将data 中的name 修改为张三,然后再来访问vm 中的name ,发现这时name 变成了张三,此时我们就验证了getter方法。
<div id="app">
<h3>姓名:{{name}}</h3>
<h3>年龄:{{age}}</h3>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
name: "张三",
age: "2岁"
}
})
</script>
验证setter方法
在上面代码中我们将name 修改为了张三,那么我们在控制台修改vm.name 为橘猫吃不胖,这时会将data 中的name 修改为橘猫吃不胖。这个变化我们并不能通过我们的代码看出来,也不能通过普通的data.name 拿到data 中name 的值,因此我们需要通过其他的途径拿到data 的值。 这时我们发现,Vue实例vm 中的_data 属性帮我们存储了data 的值,下面我们先对这个进行一个简单的验证。 思路如下,在代码中将data 在外部定义,通过验证vm._data === options.data === data 来验证我们的想法,其中options 就是new Vue({}) 中大括号{} 内的配置项,这时options.data 就是我们在外部定义的data ,具体代码如下:
<div id="app">
<h3>姓名:{{name}}</h3>
<h3>年龄:{{age}}</h3>
</div>
<script>
let data = {
name: "张三",
age: "2岁"
}
var vm = new Vue({
el: "#app",
data
})
</script>
这时我们就验证了vm._data 就是在Vue配置项中的data 。回到我们之前通过vm.name 将张三修改为橘猫吃不胖,那么我们可以通过观察vm._data.name 是否被修改来验证setter方法,从结果可以看到,答案是正确的。 因此,当我们使用vm.name = "橘猫吃不胖" 来将name 从张三修改为橘猫吃不胖时,那么就会通过setter方法将data 进行一个修改。
|