【前端面试问题】Vue响应式原理(defineProperty、defineReactive、Observer)——对象object

一、导引

1、MVVM模式

大家都知道 Vue 是 MVVM 模式,包括Model、View-Model、View。
模板:

<div>年龄:{
   
   {age}}</div>

数据变化:

this.age ++;

当数据变化时,试图也会随之改变,这里就是view-model发挥了作用。
在这里插入图片描述

2、侵入式和非侵入式

侵入式就是没有调用任何函数,直接改变数据就能实现视图的变化。
非侵入式就是通过调用函数来改变数据就能实现视图的变化。
在这里插入图片描述
那为什么Vue可以实现非侵入式的效果的——如何实现数据响应式?

二、Object.defineProperty()的认识——响应式的核心

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
可以检测对象属性变化,实现数据劫持和数据代理

1、value、writable、enumerable

var obj = {
    
    };
Object.defineProperty(obj, 'a', {
    
    
    //给a属性定义值
    value: 6,
    //是否可写
    writable: false,
    //是否可以被枚举
    enumerable: true
})
Object.defineProperty(obj, 'b', {
    
    
    //给a属性定义值
    value: 10,
    //是否可以被枚举
    enumerable: false
})
console.log('obj.a', obj.a);//6
obj.a = 10
console.log('obj.a', obj.a);//6:因为设置了不可写
for (const key in obj) {
    
    
    console.log('可枚举', key);//只会输出a,因为b设置了不可被枚举
}

在这里插入图片描述

2、get、set

var obj = {
    
    };
Object.defineProperty(obj, 'c', {
    
    
    //getter:数据劫持
    get() {
    
    
        console.log("正在访问c属性")
    },
    //setter
    set() {
    
    
        console.log('正在改变c属性')
    }
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//undefined

在这里插入图片描述

三、defineReactive函数(自定义函数)

解决obj.c为undefined的问题

因为getter函数(get)需要有一个返回值,这个返回值就是属性的值(get和value不能共存)

var obj = {
    
    };
Object.defineProperty(obj, 'c', {
    
    
    //getter:数据劫持
    get() {
    
    
        console.log("正在访问c属性");
        return 7
    },
    //setter
    set() {
    
    
        console.log('正在改变c属性')
    }
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//7

在这里插入图片描述
当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。

var obj = {
    
    };
Object.defineProperty(obj, 'c', {
    
    
    //getter:数据劫持
    get() {
    
    
        console.log("正在访问c属性");
        return 7
    },
    //setter
    set(newValue) {
    
    
        console.log('正在改变c属性', newValue)
    }
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//7 因为get返回的依旧是7

在这里插入图片描述
最终解决方案:

var obj = {
    
    };
var temp;
Object.defineProperty(obj, 'c', {
    
    
    //getter:数据劫持
    get() {
    
    
        console.log("正在访问c属性");
        return temp;
    },
    //setter
    set(newValue) {
    
    
        console.log('正在改变c属性', newValue)
        temp = newValue
    }
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//10

在这里插入图片描述
成功修改!!!

自定义defineReactive函数

//自定义defineReactive函数
function defineReactive(data, key, val) {
    
    
    Object.defineProperty(data, key, {
    
    
        //可枚举
        enumerable: true,
        //可以被delete
        configurable: true,
        //getter:数据劫持
        get() {
    
    
            console.log("正在访问" + key + "属性");
            return val;
        },
        //setter
        set(newValue) {
    
    
            console.log("正在改变" + key + "属性", newValue)
            if (val !== newValue) {
    
    
               val = newValue
            }
        }
    })
}
var obj = {
    
    };
defineReactive(obj, 'c', 10)
console.log(obj.c);//10

在这里插入图片描述

三、递归侦测对象全部属性(Observer、observe以及defineReactive是一个循环、递归的关系)

通过以上定义的defineReactive函数无法实现多层对象的劫持。

//自定义defineReactive函数(data->要访问的对象,key->对象中的属性,val->要修改的属性值【闭包】)
function defineReactive(data, key, val) {
    
    
    console.log("defineReactive", key);
    //可以传两个参数
    if (arguments.length === 2) {
    
    
        val = data[key]
    }
    observe(val);
    Object.defineProperty(data, key, {
    
    
        //可枚举
        enumerable: true,
        //可以被delete
        configurable: true,
        //getter:数据劫持
        get() {
    
    
            console.log("正在访问" + key + "属性");
            return val;
        },
        //setter
        set(newValue) {
    
    
            console.log("正在改变" + key + "属性", newValue)
            if (val !== newValue) {
    
    
                val = newValue
                observe(newValue);
            }
        }
    })
}
//设置属性能否被枚举
function def(data, key, value, enumerable) {
    
    
    Object.defineProperty(obj, key, {
    
    
        value,
        enumerable,
        writable: true,
        configurable: true
    })
}
//能将object转化为每个层级的属性都是响应式的——可以被侦测的object
class Observer {
    
    
    constructor(value) {
    
    
        //给实例添加__ob__属性,不可以被枚举,value为实例
        def(value, '__ob__', this, false);
        console.log("构造器", value)
        this.walk(value)
    }
    //遍历
    walk(value) {
    
    
        for (const key in value) {
    
    
            defineReactive(value, key)
        }
    }
}
//创建observe函数,首先判断
function observe(val) {
    
    
    if (typeof val !== 'object') return;
    var ob;
    if (typeof val.__ob__ !== 'undefined') {
    
    
        ob = val.__ob__;
    } else {
    
    
        ob = new Observer(val);
    }
    return ob

}
var obj = {
    
    
    a: {
    
    
        m: {
    
    
            n: 5
        }
    },
    b: 10
}
observe(obj);
console.log(obj.a.m.n);

在这里插入图片描述

在这里插入图片描述

以上就是Vue响应式原理(defineProperty、defineReactive、Observer)——对象object的内容,欢迎大家关注《前端面试问题》专栏。
我会将自己平时项目中常见的问题以及笔试面试的知识在CSDN与大家分享,一起进步,加油。

猜你喜欢

转载自blog.csdn.net/weixin_46318413/article/details/122763712
今日推荐