Object.defineProperty使用方法

116 阅读1分钟

背景

最近在重新学习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>