面试题之 vue 中的数据劫持是怎么回事,你晓得吗?

124 阅读2分钟

问题来了

现在Vue 是目前比较流行的前端框架,也是经常会被问到的问题,我们知道vue中的双向数据绑定是一大亮点,当数据发生改变了,也会通知我们的视图随之而更改,那么问题来了,数据一发生改变,视图咋就知道要改变了呢?

知识铺垫

在解密这个之前,我们需要先了解下Object.defineProPerty,接下来咱们就先来看下这个知识。 Object.defineProPerty 是Object类的私有属性,所以大家需要注意的是,我们对象实例是不能用这个方法的

具体语法:

var obj={};
// 给对象 obj  添加一个属性名 name,属性值是 “lili”
Object.defineProperty(obj,"name","lili");

除了上面的写法,对于值我们还可以写成对象的形式:

Object.defineProperty(obj,"name",{
    value:"li",
    // 可枚举,默认是不可枚举
    enumerable:true,
    // 是否可删除
    configurable:true,
    // 是否可修改
    writable:true
});

有时候我们想要在获取或者得到value值的时候做一些操作,那我们还可以把value 省去,添加上get 函数和 set 函数

Object.defineProperty(obj,"name",{    
    //value:"li",
    // 每次获取值的时候,就会进到get函数里面,return 是多少,咱们取到的值就是多少
    get(){
       return 100
    },
    // 每次设置值的时候,就会进到这个set 函数里面,设置的值会传进来
    set(newValue){
       console.log(newValue)
    },
    //可枚举
    enumerable:true,
    // 是否可删除
    configurable:true,
    // 是否可修改
    writable:true
});


如果想要设置值的时候,能让get 里面的返回值发生改变,我们可以借助第三个变量

var _use="";
Object.defineProperty(obj,"name",{    
    //value:"li",
    get(){
       return _use
    },
    set(newValue){
        _use=newValue;
    },
    //可枚举
    enumerable:true,
    // 是否可删除
    configurable:true,
    // 是否可修改
    writable:true
});

真正的揭秘开始了

截止到这里,我们需要了解的知识已经够了,接下来,我们还是回到最初的问题,想下数据改变了,怎么才能知道呢,其实vue2.0 就是用的Object.defineProperty 的数据劫持,只要一更改数据,就会触发set 函数,这里我们就可以来一个劫持。接下来让我们看下如果去写吧

// 模拟vue 中的data
var data={
    message:"hello",
    job:[{name:"web"}]
};

// 每次在初始化的时候,为了实现拦截,我们需要把data 里面的数据都拿到,然后用Object.defineProperty 一个个添加属性,让它有get 和set 函数,在这里我们先封装这样一个方法

// 初始的时候先调用下这个函数,然后把最初的data里面的属性都添加上get 和set
function observe(data){
    if(typeof data!="object"){
         return data;
    }
    for( var key in data){
         define(data,key,data[key]);
    }
}


function define(obj,key,value){
    // 如果传递进来的值是多层结构的,为了保证里层的数据也加上get set 所以我们统一再执行下observe
    observe(value);
    Object.defineProperty(obj,key,{
        get(){
            return value;
        },
        set(newValue){
           if(newValue !=value){
              console.log("视图发生改变了!");
              value= newValue;
           }
        }
    })
}
observe(data);
//data.message="hehe";
// data["job"][0].name="hehe";
data["job"][0].name="b";