![](https://img.haomeiwen.com/i4997712/0db606c8c539333f.jpg)
![](https://img.haomeiwen.com/i4997712/4af6c05daabf4d3a.png)
Object.defineProperty(obj,attribute,{ ... descriptor ... })
第三个参数descriptor可以有如下属性:value、writable、configurable、enumerable、get、set
其中2,3,4个参数默认是false.(writable、configurable、enumerable)
var obj= {}
Object.defineProperty(obj,"long",{
value:123,
writable:false,
enumerable:false,
configurable:false
})
console.log(obj.long);//123
前面几个参数,大致意思就是,允许更改值,允许遍历obj的属性等配置参数。下面重点要说的是get和set
var obj = {};
Object.defineProperty(obj, 'long', {
function get(){
console.log("把值返回出去")
return 888;
},
function set(newVal){
console.log("赋的新值是:" + newVal);
}
})
obj.long = 999 /*打印set里的输出语句*/
console.log(obj.long) /*打印get里的输出语句,以及888*/
------------------------------
obj = {
'brand':'BMW',
'price':3000
}
function observable (obj) {
if (!obj || typeof obj !== 'object') {
return;
}
let keys = Object.keys(obj);
keys.forEach((key) =>{
defineReactive(obj,key,obj[key])
})
return obj;
}
------------------------------
function defineReactive (obj,key,val) {
Object.defineProperty(obj, key, {
get(){
console.log(`${key}属性被读取了`);
return val;
},
set(newVal){
console.log(`${key}属性被修改了`);
val = newVal;
}
})
}
上面,已经将数据的变化,做了监听和拦截。当属性变化时,我们能知道了。next step,需要通知订阅者,也就是该属性在哪些UI组件上用到了,通知他们去刷新。
核心代码:push(obj), notify(){foreach:obj.update()}.
class Dep {
constructor(){ this.subs = [] },
//增加订阅者
addSub(sub){ this.subs.push(sub); },
//判断是否增加订阅者
depend () { if (Dep.target) { this.addSub(Dep.target) } },
//通知订阅者更新
notify(){ this.subs.forEach((sub) =>{ sub.update() }) }
}
Dep.target = null; // 全局唯一的静态属性Watcher。该类的subs也是Watcher的属性
有了Dep订阅管理器,将上面的obj属性监听改造一下。改成真正的监听器Oberver。
function defineReactive (obj,key,val) {
let dep = new Dep();
Object.defineProperty(obj, key, {
get(){
dep.depend();
console.log(`${key}属性被读取了`);
return val;
},
set(newVal){
val = newVal;
console.log(`${key}属性被修改了`);
dep.notify() //拦截到数据被改变了,订阅器去主动提醒订阅者去更新
}
})
}
订阅者Watcher,订阅器Dep,监听器Observer
/**
1:首先执行构造函数,执行this.get(),将Watcher自己,存入到订阅器中。dep.target = this.watcher
2:接着执行this.vm.data[this.exp]
2.1,也就是访问vue对象上的model属性,目的是为了触发监听器Observer里的getter
2.2,触发了监听器里的get之后,也就会去调订阅器Dep.depend(),addSub(this.watcher)。将订阅者加进去
2.3,后续,监听器Observer有数据变化的时候触发setter,会执行dep.notify.通知各个subs。也就是各个watcher
3:数据依赖Dep收集完成,对应的UI渲染也要改变。
3.1,这是要依靠watcher里的update。
3.2,get到的oldVal和Observer.notify-->sub.update-->watcher.update()的value比较。如果有变化,则cb.call进行更新
*/
class Watcher {
constructor(vm,exp,cb){ // vm是Vue对象,v-model="exp",cb是watcher绑定的更新函数
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get(); // 将自己添加到订阅器的操作
},
update(){
let value = this.vm.data[this.exp];
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
},
get(){
Dep.target = this; // 缓存自己
let value = this.vm.data[this.exp] // 强制执行监听器里的get函数
Dep.target = null; // 释放自己
return value;
}
}
网友评论