实现一个简单的defineProperty
function observe(obj) {
if (typeof obj === 'object') {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
//实现响应式
defineReactive(obj, key, obj[key]);
}
}
}
}
function defineReactive(obj, key, value) {
//针对value是对象,递归检测
observe(value);
//劫持对象的key
Object.defineProperty(obj, key, {
get() {
console.log('get' + key);
return value;
},
set(val) {
//针对所设置的val是对象
observe(val);
console.log(key + 'change');
value = val;
},
});
}
let obj = {
name: 'zm',
flag: {
book: {
name: 'js',
page: 325,
},
insert: ['lc', 'xc'],
},
};
observe(obj);
然后在控制台打印,发现了一些问题:
1)无法监测到新增属性和删除属性实现响应式
- 数组的变化无法监听到
- 递归层级深的话浪费时间
实现proxy
proxy是对目标对象进行任何操作之前有一层拦截
// 实现mvvm
/*
更新视图的方法
把数据变为响应式数据
测试数据
改变数据触发更新视图的方法
*/
// 更新视图的方法
function updateView() {
console.log('update');
}
const options = {
set(target, key, value, reciver) {
updateView();
console.log(target, key, value, reciver, '...set');
return Reflect.set(target, key, value, reciver);
},
get(target, key, reciver) {
const res = Reflect.get(target, key, reciver);
console.log(target, key, reciver, '...get');
if (isObject(target[key])) {
return reactive(res)
}
return res;
},
deleteProperty(target, key) {
return Reflect.deleteProperty(target, key);
}
}
// 缓存
const toProxy = new WeakMap();
// 把数据变为响应式数据
function reactive(target) {
if(!isObject(target)) {
return target;
};
if(toProxy.get(target)) {
return toProxy.get(target);
}
// todo
let proxyed = new Proxy(target, options);
toProxy.set(target, proxyed);
return proxyed;
}
// 测试数据
let obj = {
name: 'acd',
array: {a: 2, b: 4}
}
function isObject(t) {
return typeof t === 'object' && t !== null;
}
let reactiveObj = reactive(obj);
// 改变数据
reactiveObj.name = "changeName";
reactiveObj.array.c = 3;
新增属性也能监测到哦~
对数组操作也是可以
Object.defineProperty 拦截的是对象的属性,会改变原对象。proxy 是拦截整个对象,通过 new 生成一个新对象,不会改变原对象。
proxy 的拦截方式,除了上面的 get 和 set ,还有 11 种。
proxy使用场景
负index
let ecArrayProxy = {
get (target, key, receiver) {
let _index=key < 0 ? target.length + Number(key) : key
return Reflect.get(target, _index, receiver)
}
}
let arr=new Proxy([1,2,3], ecArrayProxy)
arr[-1] //3
表单验证,进行拦截
let ecValidate = {
set (target, key, value, receiver) {
if (key === 'age') {
//如果值小于0,或者不是正整数
if (value <0 || !Number.isInteger(value)) {
throw new TypeError('请输入正确的年龄');
}
}
return Reflect.set(target, key, value, receiver)
}
}
let obj=new Proxy({age:18},ecValidate)
obj.age='少年'
https://juejin.im/post/5edf576ff265da76de5ceec6