一、对象属性修改的监听、Object.defineProperty、Proxy、Reflect
1、通过 Object.defineProperty
将数据描述符转成属性描述符,然后进行监听
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get: function() {
console.log(`监听到obj对象的${key}属性被访问了`)
return value
},
set: function(newValue) {
console.log(`监听到obj对象的${key}属性被设置值`)
value = newValue
}
})
})
- 【理解关键点】闭包捕获的是一个
词法环境
,不是捕获具体某一个值。 - 【缺点】①
Object.defineProperty
并不是专门为了监听函数修改而设计的,功能上有些不足,有些操作无法监听 ②这种监听方式,改变了原有 obj 对象的描述符,将数据描述符改成了属性描述符,做法有点欠妥。
2、通过 类:Proxy
实现对象的监听(receiver暂存,后面解答)?
const obj = {
name: "why",
age: 18,
};
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
console.log(`监听到obj对象的${key}属性被访问了`);
return target[key];
},
set: function (target, key, value, receiver) {
console.log(`监听到obj对象的${key}属性被设置了`);
target[key] = value;
},
});
console.log(objProxy.name);
objProxy.name = "lsp";
console.log(objProxy.name);


3、为什么会出现 Reflect
? Reflect
是类吗?Reflect
常见的是和哪个类进行搭配使用?
-【出现背景】Object只是一个对象构造函数,JavaScript早期有些内置方法加给了Object,让Object承受了很多它不该承受的作用。所以ES6出现Reflect,将Object不该承受的方法剥离出来,更加符合单一职责的原则。
- 【用途】Reflect大部分方法和Object的方法相同。
-【Refect是类吗】Reflect是一个内置的对象,不是一个函数对象,因此它不可以被 new。
-【搭配】Reflect经常和Proxy进行搭配使用

4、Refect 和 Proxy搭配使用案例?
const obj = {
name: "why",
age: 18,
};
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
console.log(`监听到obj对象的${key}属性被访问了`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`监听到obj对象的${key}属性被设置了`);
Reflect.set(target, key, value, receiver);
},
});
console.log(objProxy.name);
objProxy.name = "lsp";
console.log(objProxy.name);
5、简述Proxy中 receiver 参数的作用?
-
当原始对象obj中有访问器属性时,使用 receiver 就也可以监听到。
image.png
二、响应式实现(必须要默写出来)
1、使用Proxy实现,思路很重要!
- 【关键步骤】①实现对象的set和get监听 ②自动收集使用的函数 ③用合理的数据结构存储关系
- 【代码感悟】数据结构设计之美,让程序分层结构非常合理。棒
// 保存当前需要收集的响应式函数
let activeReactiveFn = null
/**
* Depend优化:
* 1> depend方法
* 2> 使用Set来保存依赖函数, 而不是数组[]
*/
class Depend {
constructor() {
this.reactiveFns = new Set()
}
// addDepend(reactiveFn) {
// this.reactiveFns.add(reactiveFn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 封装一个响应式的函数
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
// 根据target对象获取map的过程
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
// 根据key获取depend对象
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
function reactive(obj) {
return new Proxy(obj, {
get: function(target, key, receiver) {
// 根据target.key获取对应的depend
const depend = getDepend(target, key)
// 给depend对象中添加响应函数
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target, key, receiver)
},
set: function(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
// depend.notify()
const depend = getDepend(target, key)
depend.notify()
}
})
}
// 监听对象的属性变量: Proxy(vue3)/Object.defineProperty(vue2)
const objProxy = reactive({
name: "why", // depend对象
age: 18 // depend对象
})
const infoProxy = reactive({
address: "广州市",
height: 1.88
})
watchFn(() => {
console.log(infoProxy.address)
})
infoProxy.address = "北京市"
const foo = reactive({
name: "foo"
})
watchFn(() => {
console.log(foo.name)
})
foo.name = "bar"
2、使用Object.defineProperty实现,思路很重要!
// 保存当前需要收集的响应式函数
let activeReactiveFn = null
/**
* Depend优化:
* 1> depend方法
* 2> 使用Set来保存依赖函数, 而不是数组[]
*/
class Depend {
constructor() {
this.reactiveFns = new Set()
}
// addDepend(reactiveFn) {
// this.reactiveFns.add(reactiveFn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 封装一个响应式的函数
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
// 根据target对象获取map的过程
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
// 根据key获取depend对象
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
function reactive(obj) {
// {name: "why", age: 18}
// ES6之前, 使用Object.defineProperty
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get: function() {
const depend = getDepend(obj, key)
depend.depend()
return value
},
set: function(newValue) {
value = newValue
const depend = getDepend(obj, key)
depend.notify()
}
})
})
return obj
}
// 监听对象的属性变量: Proxy(vue3)/Object.defineProperty(vue2)
const objProxy = reactive({
name: "why", // depend对象
age: 18 // depend对象
})
const infoProxy = reactive({
address: "广州市",
height: 1.88
})
watchFn(() => {
console.log(infoProxy.address)
})
infoProxy.address = "北京市"
const foo = reactive({
name: "foo"
})
watchFn(() => {
console.log(foo.name)
})
foo.name = "bar"
foo.name = "hhh"
网友评论