最近开始看vue.js的原理以及源码(真的是没有无缘无故去读源码,为了面试也好,为了和框架知已知彼也好,多学点总没错的吧😂),关于数据侦听这块,vue.js主要是用了Object​.define​Property()
不过Object​.define​Property()也存在很多缺陷,但是对这个属性方法不熟悉的我,阅读源码时遇到了很多不便利,查了网上资料,自己整理一下Object​.define​Property()的用法
Object​.define​Property()用于在一个对象上新增属性或者修改已有属性,举个🌰

1
2
3
4
5
6
7
8
let myObj = {       
name: 'aiyaya',
}
let newObj = Object.defineProperty(myObj, 'age', {
value: 18
})
console.log(myObj) // {name: "aiyaya", age: 18}
console.log(newObj) // {name: "aiyaya", age: 18}

对象的属性描述符(就是对象属性的属性)一般分为两种,一种就是数据属性描述符(属性的读写属性一类),另一种就是存取描述符(给属性修改值或者获取属性值),
主要看存取描述符:
get

一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为undefined

set

一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。

试用一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let oldValue = 18   
let myObj = {
name: 'aiyaya',
}
let newObj = Object.defineProperty(myObj, 'age', {
get: function() {
console.log('get')
return oldValue
},
set: function(newValue) {
console.log('set')
oldValue = newValue
}
})
console.log(newObj.age) // get 18
newObj.age = 20; // set

对于vue中对象属性的观察来说,其实就是利用了Object​.define​Property()方法,get收集依赖,set触发依赖,我的理解,所谓收集依赖就是找到所有用到改变数据的地方,触发依赖就是修改这些数据
仿照vue.js中的写法,把上面的🌰重写一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Observer函数   
function Observer(value) {
this.value = value;
this.walk(value) }
// 由于一个object的属性可能是一个object所以需要递归
Observer.prototype.walk = function(obj){
var keys = Object.keys(obj);
for(let i = 0, l = keys.length; i < l; i++) {
this.convert(keys[i], obj[keys[i]])
}
};
Observer.prototype.convert = function (key, val) {
Object.defineProperty(myObj, key, {
enumerable: true,
configurable: true,
get: function () {
console.log('get: ' + key);
return val
},
set: function (newVal) {
console.log('set: ' + key);
console.log('新的' + key + ' = ' + newVal)
if (newVal === val) return;
val = newVal
}
})
}
let myObj = {
name: 'aiyaya',
type: {
age: 18,
color: 'red'
}
};
let app = new Observer(myObj);

然后再看下Object​.define​Property()的缺陷: 在对象上新增和删除属性都无法被追踪到。
执行代码


20191001 火曜日 更新
纸上得来终觉浅 昨天在项目中遇到了关于这个object.defineProperty()中的这个坑。
需求内容是:根据后端传过来的数据渲染表格数据,然后当再次从输入框输入相应查询数据时,将对应行数据的状态status状态置为正常。
按照需求,当第二次输入查询对应内容的时候,我将tableData数组中对应的那一条对象数据status属性改成了正常,打印数据也是对的,但是表格UI界面没有任何变化,我就纳闷了….
后来仔细看了接口数据,发现后段给的数据中,也就是我一开始拿到的tableData中根本没有status字段,相当于触及了object.defineProperty()无法侦听对象的属性新增或者删除这个情况。bug算是找到出处了,接下来就是要解决,baidu了一波,发现了vue中自带解决这个问题的方法,使用
Vue.set( target, propertyName/index, value )

向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = ‘hi’)

或者不用这个方法的话,就是一开始就给数据加上这个status属性,赋值为空的字符串也行,遇到bug不可怕,遇不到bug才可怕吧。
谨此一记。😄