Skip to content

Commit

Permalink
proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyawei committed Apr 7, 2019
1 parent b18a584 commit 53ea83d
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 13 deletions.
84 changes: 74 additions & 10 deletions JavaScript/Proxy/1.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,77 @@
let ironman = {
hobbies: ['1', '23', '56']
let data = {
name: '渣渣辉'
};
class Dep {
constructor() {
this.subs = new Map();
}
addSub(key, sub) {
const hasSub = this.subs.get(key);
if (hasSub) {
hasSub.add(sub);
} else {
this.subs.set(key, new Set([sub]));
}
}
notify(key) {
if (this.subs.get(key)) {
this.subs.get(key).forEach(sub => {
sub.update();
});
}
}
}
let ironmanProxy = new Proxy(ironman.hobbies, {
set (target, property, value) {
target[property] = value
console.log('change....', property, value)
return true
class Watcher {
constructor(obj, key, cb) {
this.obj = obj;
this.key = key;
this.cb = cb; // 回调
this.value = this.get(); // 获取老数据
}
get() {
Dep.target = this;
let value = this.obj[this.key];
Dep.target = null;
return value;
}
// 将订阅者放入待更新队列等待批量更新
update() {
let newVal = this.obj[this.key];
if (this.value !== newVal) {
this.cb(newVal);
}
}
})
}
function Observer(obj) {
Object.keys(obj).forEach(key => { // 做深度监听
if (typeof obj[key] === 'object') {
obj[key] = Observer(obj[key]);
}
});
let dep = new Dep();
let handler = {
get: function (target, key, receiver) {
Dep.target && dep.addSub(key, Dep.target);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
let result = Reflect.set(target, key, value, receiver);
dep.notify(key);
return result;
}
};
return new Proxy(obj, handler)
}
function print1(data) {
console.log('我系', data);
}
function print2(data) {
console.log('我今年', data);
}
data = Observer(data);
new Watcher(data, 'name', print1);
data.name = '杨过'; // 我系 杨过

ironmanProxy.push('wine')
console.log(ironman.hobbies)
new Watcher(data, 'age', print2);
data.age = '24'; // 我今年 24
console.log(data);
126 changes: 123 additions & 3 deletions vue/mvvm/1.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Vue 官方的响应式原理图镇楼。
}
</script>
```
我们知道在 Vue 中,会通过 `Object.defineProperty`将 data 中定义的属性做数据劫持,用来支持相关操作的发布订阅。而在我们的例子里,data 中只定义了 list 为一个空数组,所以 Vue 会对它进行劫持,并添加对应的 getter/setter。
我们知道在 Vue 中,会通过 `Object.defineProperty` 将 data 中定义的属性做数据劫持,用来支持相关操作的发布订阅。而在我们的例子里,data 中只定义了 list 为一个空数组,所以 Vue 会对它进行劫持,并添加对应的 getter/setter。

所以在 1 s 的时候,通过 `this.list = [{text: 666}, {text: 666}, {text: 666}]` 给 list 重新赋值,便会触发 setter,进而通知对应的观察者(这里的观察者是模板编译)做更新。

Expand Down Expand Up @@ -401,7 +401,7 @@ export function popTarget () {
}
```
#### Watcher
单个看 Dep 可能不太好理解,我们结合 Watcher 一起分析
单个看 Dep 可能不太好理解,我们结合 Watcher 一起来看
``` javascript
// observer/watcher.js

Expand Down Expand Up @@ -510,7 +510,127 @@ export default class Watcher {
}
}
```
到这里,我们可以大概总结一些整个响应式系统的流程:第一步当然是通过 observer 进行数据劫持,然后在需要订阅的地方(如:模版编译),添加观察者(watcher),并立刻通过取值操作触发指定属性的 getter 方法,从而将观察者添加进 Dep (利用了闭包的特性),然后在 Setter 触发的时候,进行 notify,通知给所有观察者进行相应的 update。
到这里,我们可以大概总结一些整个响应式系统的流程,也是我们常说的 **观察者模式**:第一步当然是通过 observer 进行数据劫持,然后在需要订阅的地方(如:模版编译),添加观察者(watcher),并立刻通过取值操作触发指定属性的 getter 方法,从而将观察者添加进 Dep (利用了闭包的特性,进行依赖收集),然后在 Setter 触发的时候,进行 notify,通知给所有观察者并进行相应的 update。
## Proxy
> Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。— 阮一峰老师的 [ECMAScript 6 入门](http://es6.ruanyifeng.com/#docs/proxy)
我们都知道,Vue 3.0 要用 `Proxy` 替换 `Object.defineProperty`,那么这么做的好处是什么呢?
好处是显而易见的,比如上述 Vue 现存的两个问题,不能响应对象属性的添加和删除以及不能直接操作数组下标的问题,都可以解决。盎然也有不好的,那就是兼容性问题,而且这个兼容性问题 babel 还无法解决。
### 基础用法
我们用 Proxy 来简单实现一个数据劫持。
``` javascript
let obj = {};
// 代理 obj
let handler = {
get: function(target, key, receiver) {
console.log('get', key);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log('set', key, value);
return Reflect.set(target, key, value, receiver);
},
deleteProperty(target, key) {
console.log('delete', key);
delete target[key];
return true;
}
};
let data = new Proxy(obj, handler);
// 代理后只能使用代理对象 data,否则还用 obj 肯定没作用
console.log(data.name); // get name 、undefined
data.name = '尹天仇'; // set name 尹天仇
delete data.name; // delete name
```
在这个栗子中,obj 是一个空对象,通过 Proxy 代理后,添加和删除属性也能够得到反馈。再来看一下数组的代理:
``` javascript
let arr = ['尹天仇', '我是一个演员', '柳飘飘', '死跑龙套的'];
let array = new Proxy(arr, handler);
array[1] = '我养你啊'; // set 1 我养你啊
array[3] = '先管好你自己吧,傻瓜。'; // set 3 先管好你自己吧,傻瓜。
```
数组索引的设置也是完全 hold 得住啊,当然 Proxy 的用处也不仅仅是这些,支持拦截的操作就有 13 种。有兴趣的同学可以去看 [阮一峰老师的书](http://es6.ruanyifeng.com/#docs/proxy),这里就不再啰嗦。
### Proxy 实现观察者模式
我们前面分析了 Vue 的源码,也了解了观察者模式的基本原理。那用 Proxy 如何实现观察者呢?我们可以简单写一下:
``` javascript
class Dep {
constructor() {
this.subs = new Set();
// Set 类型,保证不会重复
}
addSub(sub) { // 添加订阅者
this.subs.add(sub);
}
notify(key) { // 通知订阅者更新
this.subs.forEach(sub => {
sub.update();
});
}
}
class Watcher { // 观察者
constructor(obj, key, cb) {
this.obj = obj;
this.key = key;
this.cb = cb; // 回调
this.value = this.get(); // 获取老数据
}
get() { // 取值触发闭包,将自身添加到dep中
Dep.target = this; // 设置 Dep.target 为自身
let value = this.obj[this.key];
Dep.target = null; // 取值完后 设置为nul
return value;
}
// 更新
update() {
let newVal = this.obj[this.key];
if (this.value !== newVal) {
this.cb(newVal);
this.value = newVal;
}
}
}
function Observer(obj) {
Object.keys(obj).forEach(key => { // 做深度监听
if (typeof obj[key] === 'object') {
obj[key] = Observer(obj[key]);
}
});
let dep = new Dep();
let handler = {
get: function (target, key, receiver) {
Dep.target && dep.addSub(Dep.target);
// 存在 Dep.target,则将其添加到dep实例中
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
let result = Reflect.set(target, key, value, receiver);
dep.notify(); // 进行发布
return result;
}
};
return new Proxy(obj, handler)
}
```
代码比较简短,就放在一块了。整体思路和 Vue 的差不多,需要注意的点在于:。
再看一下运行结果:
``` javascript
let data = {
name: '渣渣辉'
};
function print1(data) {
console.log('我系', data);
}
function print2(data) {
console.log('我今年', data);
}
data = Observer(data);
new Watcher(data, 'name', print1);
data.name = '杨过'; // 我系 杨过

new Watcher(data, 'age', print2);
data.age = '24'; // 我今年 24
```
## MVVM
### 概念

0 comments on commit 53ea83d

Please sign in to comment.