目录
Vue2.0 :
通过 Object.defineProperty 方法属性拦截的方式,把 data 对象里每个数据的读写转化成 getter/setter,当数据变化时通知视图更新。
Object.defineProperty()
语法:
Object.defineProperty(obj, property, descriptor)
下面重点讲述第三个参数:descriptor:
在JS中对象具有两种属性,分别是数据属性和访问器属性,
所以其描述符也根据属性分类,分为数据描述符 和 访问器描述符 。
在使用描述符时,必须是两种形式之一,且两者不能同时使用。
当使用了getter或setter方法,不允许使用writable和value这两个属性(如果使用,会直接报错滴)
数据描述符
具有以下可选的键值:
configurable:表示该属性能否通过delete删除,能否修改属性的特性或者能否修改访问器属性,默认为false。
enumerable:表示该属性是否可以枚举,即可否通过for..in访问属性。默认为false。value:表示该属性的值。可以是任何有效的JS值。默认为undefined。
writable:表示该属性的值是否可写,默认为false。
访问器描述符
具有以下可选的键值:
configurable:表示该属性能否通过delete删除,能否修改属性的特性或者能否修改访问器属性,默认为false。
enumerable:表示该属性是否可以枚举,即可否通过for..in访问属性。默认为false。get:在读取属性时调用的函数,默认值为undefined。
set:在写入属性时调用的函数,默认值为undefined。
const user = {
age: 18,
wife: {
name: 'xiao',
age: 17
}
}
var newName = 'wang'
Object.defineProperty(user, 'name', {
get () {
console.log('get方法调用了')
return newName
},
set (val) {
console.log('set方法调用了')
newName = val
}
})
console.log(user.name)
user.name = 'lao wang'
console.log(user.name)
打印结果如下:
new Proxy()
new Proxy() 把目标对象变成代理对象
参数1: user -----> target目标对象
参数2: handler --> 处理器对象,用来监视数据,及数据的操作
const user = {
name: 'wang',
age: 18,
wife: {
name: 'xiao',
age: 17
}
}
const proxyUser = new Proxy(user, {
get (target, property) { // 读取目标对象的某个属性值
console.log('get方法调用了')
return Reflect.get(target, property)
},
set (target, property, val) { // 修改/添加属性值
console.log('set方法调用了')
return Reflect.set(target, property, val)
},
deleteProperty(target, property) { // 删除目标对象的某个属性
console.log('delete方法调用了')
return Reflect.deleteProperty(target, property)
}
})
// 通过代理对象:
// 查---获取目标对象中的某个属性值
console.log(proxyUser.name)
// 改---更新目标对象中的某个属性值
proxyUser.name = 'lao wang'
console.log(user)
// 增---向目标对象中添加一个新的属性值
proxyUser.sex = 'male'
console.log(user)
// 删---删除目标对象的某个属性值
delete proxyUser.name
console.log(user)
打印结果如下:
Object.defineProperty的缺陷
const data = {
name: '',
};
// 遍历对象,对其属性值进行劫持
Object.keys(data).forEach(function(key) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
},
set: function(newVal) {
// 当属性值发生变化时我们可以进行额外操作
console.log(`大家好,我系${newVal}`);
},
});
});
data.name = '渣渣辉';
虽然Object.defineProperty通过为属性设置getter/setter能够完成数据的响应式,但是它并不算是实现数据的响应式的完美方案,主要缺陷有:
1. 无法检测到对象属性的新增或删除(直接新增属性、删除属性,页面不会更新);
2. 无法监听数组变化(直接通过下标修改数组,界面不会自动更新。)
无法检测到对象属性的新增或删除
<p>{{obj}}</p>
<p>{{arr}}</p>
data(){
return {
obj:{
a:2
}
}
},
mounted () {
this.obj.b = 222
},
<!-- obj显示结果 -->
//obj:{ "a": 2 }
解决方法:
(1)增加属性:
this.obj = Object.assign({},this.obj, { b: 1, e: 2 })
this.obj = {...this.obj,...{ b: 3, e: 2 }}
this.$set(this.obj,'f',0) // 或 vue.set(this.obj,'f',0)
(2)删除属性:
this.$delete(obj, propertyName/index) // 或 vue.delete(obj, propertyName/index)
无法监听数组变化
<p>{{arr}}</p>
data(){
return {
arr:[1,2]
}
},
mounted () {
this.arr[0]= 3333
},
<!-- arr显示结果 -->
arr:[ 1, 2 ]