Vue2响应式原理(一)Object.defineProperty

前言 为什么要学原理

我们知道vue中的数据可以做到响应式,那么响应式的原理是什么大家知道吗?

有同学可能会说,我管他原理是什么,我会用就行。但是其实不然,我们开发中经常会遇到,明明修改了一个数组或对象中的值,但是页面上却没有发生变化,一时半会也不知道解决办法,非常伤脑筋。
但是如果大家知道了响应式的原理,这个问题就是手到擒来,分分钟解决掉,接下来我们就开始学习吧~

说到原理,我们不得不先说起一个方法,那就是 Object.defineProperty,响应式的实现跟它有莫大的关系。

注:全篇的内容是看了天禹老师讲的vue课程后总结的笔记!!!


有时间的话后期还会更新Vue3的响应式原理以及Vue2和Vue3的对比

1、Object.defineProperty

首先我们声明一个对象 person:

let person = {
    
    
  name: "张三",
  sex: "男",
};

这时候我们想给person新增一个 age 属性,我们不直接在定义person的时候写age属性,而是通过Object.defineProperty方法进行添加。

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。


语法: Object.defineProperty(obj, prop, descriptor)

简单来说,这个方法就是给一个对象上定义新属性的,并且传入三个参数。

  • 第一个参数是 要定义属性的对象。
  • 第二个参数是 要定义或修改的属性的名称
  • 第三个属性是一个对象,里面写一些(配置)属性

1.1、可枚举的

Object.defineProperty(person, "age", {
    
    
  value: 18, // value这个字段名是固定的,表示新增属性age的值
});

这个时候我们就给person对象新增了一个age属性,值为18。我们在控制台打印一下这个person对象。
在这里插入图片描述
我们可以看到打印的结果,在控制台中 age属性的颜色和name、sex属性的颜色不太一样,这表示什么呢?这里表示age这个属性是不可以被枚举的,也就是不可遍历的。

console.log("Object.keys---", Object.keys(person));

for (const key in person) {
    
    
  console.log("for in遍历---", key, "---", person[key]);
}

在这里插入图片描述


如果我们想让age是可以被遍历的,我们就要在第三个参数配置项里多写一个字段:

Object.defineProperty(person, "age", {
    
    
  value: 18,
  enumerable: true, // 控制属性是否可以枚举,默认是false
});

这时候再去控制台看,age属性的颜色和其他属性一样,而且是可以被遍历的。
在这里插入图片描述



1.2、可修改的

我们想修改person中的属性,如果是person定义时直接写的,那我们可以修改成功:
在这里插入图片描述


但是我们想修改通过Object.defineProperty定义的属性,直接修改是不行的:
在这里插入图片描述


如果我们想修改成功,那么也要在第三个参数的配置项里多添加一个属性:

Object.defineProperty(person, "age", {
    
    
  value: 18,
  enumerable: true,
  writable: true, // 控制属性是否可以被修改,默认false
});

在这里插入图片描述


1.3、可删除的

直接在person上定义的属性可以删除,用defineProperty定义的默认不可删除:
在这里插入图片描述


如果我们想删除成功,那么也要在第三个参数的配置项里多添加一个属性:

Object.defineProperty(person, "age", {
    
    
  value: 18,
  enumerable: true,
  writable: true, 
  configurable: true // 控制属性是否可以被删除,默认false
});

在这里插入图片描述

2、一个重要的例子

let number = 18;

let person  = {
    
    
  name: "张三",
  sex: "男",
  age: number,
}

这里有个需求,person的age属性取的是number的值。我们如果按照上述代码写,看似是没问题的,但是当我们改变number的值后,person中的age属性的值会随着number变化吗?答案是不会的,我相信,只要学过js的人都知道不会变。
在这里插入图片描述

但是如果我想让number改变的时候,person.age的值也会 自动的 随之变化,应该怎么做呢?

接下来我们用defineProperty来实现:
Object.defineProperty的第三个参数(配置项)中不止可以写上述的几个属性,还可以写函数:

get 函数:当有人读取person的当前属性时,get函数就会被调用,且返回值就当前属性的值

Object.defineProperty(person, "age", {
    
    
 // 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
  get() {
    
    
    return "hello"; // 随便写个返回值测试一下
  },
});

在这里插入图片描述

注意:鼠标悬浮在age后面的三个点上的时候:
在这里插入图片描述

invoke 在这里可以理解为映射的意思,getter就是指的get函数。


我们可以看到当前person身上是有age属性的,但是用三个点给隐藏了。意思就是,我(person)身上有age属性,但是你要是想知道age属性的值,就得点击一下age后面的三个点,我得去问问getter,getter的返回值是多少,我这里的 age 就是多少。

各单位注意,这age后面的 “三个点” 划重点了!!!!!!!!一定要记住这里的age的样子

点击age后面的三个点以后:
在这里插入图片描述
我们发现上面的hello就是我们所写的getter的返回值。

那么接下来我们继续实现我们的需求,我们不能让getter的值返回‘hello’啊,应该返回number的值:

let number = 18;
let person = {
    
    
  name: "张三",
  sex: "男",
};

Object.defineProperty(person, "age", {
    
    
  get() {
    
    
    return number;
  },
});

在这里插入图片描述
每一次访问age属性,都会触发get函数的调用。

除了可以写get以外,还可以写set函数:

set 函数:当有人修改person的当前属性时,set函数就会被调用,且会收到修改的具体值

Object.defineProperty(person, "age", {
    
    
  get() {
    
    
    return number;
  },

  // 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
  set(value) {
    
    
    console.log(`有人修改了age的值,且值为${
      
      value}`);
    number = value;
  },
});

在这里插入图片描述
我们可以看到当修改person.age的值以后,number也会跟着改变。

这个时候我们就实现了当number改变的时候,person.age会自动跟着改变;当perosn.age改变的时候,number也会跟着改变。

了解了Object.defineProperty以后,接下来我们讲一下Vue中的响应式原理,看我下一篇文章哦~

猜你喜欢

转载自blog.csdn.net/qq_42667613/article/details/123575795