背景
最近在重新学习vue2的源码,vue2的响应实现的核心原理是Object.defineProperty,其使用有一些需要注意和深思的地方。故记录下。
简单对象的响应式实现
<script>
function defineReactive(target, key, value) {
// value作为函数内部的局部变量作为get/set的中转
return Object.defineProperty(target, key, {
enumerable: true,
configurable: true,
get() {
console.log('调用了getter方法');
return value;
// return target[key]; target[key]触发get,死循环
},
set(newVal) {
console.log('调用了setter方法');
value = newVal;
// target[key] = newVal; target[key]触发set,死循环
}
});
}
const ss = defineReactive({}, 'a', 1);
console.log(ss.a);
ss.a = 2;
</script>
1. js函数的参数传递是值传递
const a ={c:1};
function fn(a,b){
a = a || 2;//修改了引用
console.log(a);
}
fn(null,2)
2. js的参数是一个局部的变量,一般情况下函数执行完成,作用域栈弹出。如果有闭包的情况下,内函数可以引用父函数的局部变量、以及全局变量。
(function(){
let cc = {};
// 内部引用局部变量
return {
aa(){},// 特权方法
bb(){}
}
})()
3. {}.a 触发其get方法,不能在get内部再使用{}.a的写法,否则触发死循环。vue就是new watcher(vm,'c.a',() => {})中先把当前的对象设置到Dept.target属性上,然后读取对象的属性,触发了get方法,在get方法中把dep.target加入到依赖收集的数组中
function defineReactive(target, key, value) {
return Object.defineProperty(target, key, {
enumerable: true,
configurable: true,
get() {
return target[key]; // 死循环的写法
},
set(newVal) {
console.log('调用了setter方法');
value = newVal;
// target[key] = newVal;
}
});
}
4. {}.a = xxx触发其set方法,不能再set内部再调用{}.a = xxx,否则触发死循环
<script>
function defineReactive(target, key, value) {
return Object.defineProperty(target, key, {
enumerable: true,
configurable: true,
get() {
console.log('调用了getter方法');
return value;
// return target[key];
},
set(newVal) {
target[key] = newVal;// 死循环写法
}
});
}
const ss = defineReactive({}, 'a', 1);
console.log(ss.a);
ss.a = 2;
</script>