vue2.x版本中Object.defineProperty对象属性监听和关联

王子腾
• 阅读 1056

前言

在vue2.x版本官方文档中
深入响应式原理 https://cn.vuejs.org/v2/guide/reactivity.html一文的解释当中,Object.defineProperty将声明响应式 property数据的状态转换为getter和setter。

Object.defineProperty基本使用和概念

官方解释的概念是

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

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

Object.defineProperty(obj, prop, descriptor)

obj: 要在其上定义属性的对象
prop: 要定义或修改的属性的名称或Symbol
descriptor: 定义或修改的属性的描述符

* @descriptor
*
* configurable    为 true 时,属性才能重新被定义,默认为 false。
* enumerable    为 true 时,该属性才能够出现在对象的枚举属性中,此时才能通过for in遍历属性。默认为 false。
* writable    为 true 时,value属性值才能被修改。默认为 false,此时value属性值为只读状态。
* value    该属性对应的初始值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
* get    一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行。默认为 undefined。
* set    一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为 undefined。
* 注意事项:get 和 set 它们与value和writable是互斥的。一旦使用它们,则这个属性就没有保存属性值的能力

Object.defineProperty新增对象属性

let userInfo = {
  age: "11",
};
console.log("初始userInfo:", userInfo);
// Object.defineProperty新增对象属性
Object.defineProperty(userInfo, "name", {
  value: "zhangsan",
  enumerable: true, //将enumerable设为true 才能够出现在对象的枚举属性
});
console.log("设置name后的userInfo:", userInfo);
console.log("设置name后的userInfo属性:", Object.keys(userInfo));

当enumerable设置为false的时候,我们通过Object.keys枚举属性,并不能获取到name属性

使用Object.defineProperty修改和监听属性值的变化

let userInfo = {
  age: "11",
};

let initAge = null;
Object.defineProperty(userInfo, "age", {
  enumerable: true,
  configurable: true,
  get: function () {
    console.log("get属性方法,当前对象:", this);
    return initAge;
  },
  set: function (newValue) {
    console.log("set属性方法", newValue);
    initAge = newValue;
  },
});
userInfo.age = "30";
console.log("最后userInfo", userInfo);

通过get和set操作对象属性类似于我们在vue开发过程中的计算属性computed详解,通过get和set对当前对象进行设置属性值

Object.defineProperty小结

  • 由上面的的例子我们可以知道,当我们使用Object.defineProperty操作对象的时候,都是直接通过对象属性进行操作,而不是对整个对象进行修改,删除和新增,查询属性
  • 在使用Object.defineProperty方法操作的时候,一些默认的属性选项,需要我们注意

Object.defineProperty和vue2.x的联系

回到开头提到的官方文档中描述的,响应式原理中,如下图

vue2.x版本中Object.defineProperty对象属性监听和关联

图片源自vue官方文档深入响应式原理: https://cn.vuejs.org/v2/guide/reactivity.html

当组件渲染时,通过对象劫持,遍历data状态,那么需要考虑的点是:

假如在组件运行时,我们需要额外添加状态的时候,添加一个新的状态,或者在原有的状态下,添加一个新的属性会发生什么,这个新的状态并不在组件劫持的状态之内:

<p>
  <span style="cursor: pointer; color: red" @click="handleUser"
    >点击修改不在data状态下的值</span
  >
</p>
<p>用户信息: name {{ defaultUser.name }}, age: {{ defaultUser.age }}</p>



data() {
    return {
      defaultUser: {
        name: "张三",
      },
    };
  },
  
methods: {
    handleUser() {
      this.defaultUser.age = "23";
      console.log("数据已经发生更改,但是视图没有发生更新", this.defaultUser);
    },
  },
  

当我们点击按钮的时候,视图中的ui并没有发生更改,根据官方的文档,我们可以使用 $set 针对新增的状态进行修改

<p>
      <span style="cursor: pointer; color: red" @click="handleSet"
        >点击$set修改属性值</span
      >
    </p>
<p>通过$set修改的值:{{ setData.name }}</p>


data() {
    return {
      setData: {},
    };
  },

methods: {
    handleSet() {
      this.$set(this.setData, "name", "张三");
    },
  },

除了后续新增的状态无法进行修改之外,使用Object.defineProperty劫持的数据,对数组本身可以操作的到,但是会存在一定的性能问题,具体不做详细解释,可以参考以下博文,感谢该博主的博文,vue的框架的作者尤大也做了解释:

记一次思否问答的问题思考:Vue为什么不能检测数组变动 https://segmentfault.com/a/1190000015783546

结语

根据以上例子结合vue响应式原理,我们可以知道在vue2.x版本中Object.defineProperty存在以下问题:

1.监听数组变化下存在性能问题
2.Object.defineProperty只能劫持对象的属性,并且针对新增的data状态,无法劫持到,只能通过vue的扩展方法$set进行处理

扩展vue3使用的proxy

阮一峰-ECMAScript 6 入门
查看最简单的例子和使用方法

下文截取自:阮一峰-ECMAScript 6 入门----> Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”

var obj = new Proxy({}, {
  get: function (target, propKey, receiver) {
    console.log(`getting ${propKey}!`);
    return Reflect.get(target, propKey, receiver);
  },
  set: function (target, propKey, value, receiver) {
    console.log(`setting ${propKey}!`);
    return Reflect.set(target, propKey, value, receiver);
  }
});

Object.defineProperty对比Proxy个人理解

  • Proxy是针对整个对象的变化进行检测和拦截,可以知道对象的属性是新增,删除,还是修改等,都可以通过通过get和set进行监听得到
  • Object.defineProperty只针对对象得属性进行操作,结合vue2.x,组件渲染完成,后续新增得属性,没办法劫持到
  • Object.defineProperty针对属性,Proxy针对整个对象得操作

更多Object.defineProperty和Proxy的对比,以及vue3如果使用proxy实现对象的监听,感兴趣的同学可以去搜索相关博文,后续有时间再整理。

源码地址

文章个人博客地址:vue2.x版本中Object.defineProperty对象属性监听和关联

欢迎关注公众号:程序员布欧,不定期更新一些前端入门文章

创作不易,转载请注明出处和作者。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(