3分钟了解vue数据劫持的原理实现


Posted in Javascript onMay 01, 2019

目的: 了解Object.defineProperty如何实现数据劫持

大致原理是这样的:

  1. 定义一个监听函数,对对象的每一个属性进行监听
  2. 通过Object.defineProperty对监听的每一个属性设置get 和 set 方法。
  3. 对对象实行监听
  4. 对对象内嵌对象进行处理
  5. 对数组对象进行处理

1. 先定义一个对象

let obj = {
 name: 'jw'
}

2. 定义一个监听函数

/**
* 判断监听的是否是对象
* 如果是对象,就遍历,并且对每个属性进行定义get 和 set
*/

function observer(obj) {
 if(typeof obj === 'object') {
  for (let key in obj) {
  // defineReactive 方法设置get和set,见第三步
   defineReactive(obj, key, obj[key]);
  }
 }
}

3.定义一个函数,处理每个属性

function defineReactive(obj, key, value) {
 Object.defineProperty(obj, key, {
  get() {
   return value;
  },
  set(val) {
   console.log('数据更新了')
   value = val;
  }
 })
}

ok. 到这里初版已经实现了。尝试一下吧

observer(obj);
obj.name = 'haha'

控制台输出:
//数据更新了

以上已经实现设置obj的属性的时候,被监听到,并且可以去执行一些代码了。但是,如果对象里面嵌入了对象呢?比如:

let obj = {
 name: 'jw',
 age: {
  old: 60
 }
}

执行以下代码

observer(obj);
obj.age.old = '50'

控制台输出: 空

4.对监控的obj进行迭代处理

// 修改defineReactive , 添加一行代码
function defineReactive(obj, key, value) {
 // 如果对象的属性也是一个对象。迭代处理
 observer(value);
 Object.defineProperty(obj, key, {
  //....
 })
}

再执行以下代码:

observer(obj);
obj.age.old = '50'

控制台输出:
//数据更新了

可惜的是,如果对象是一个数组,Object.defineProperty就无法起作用了,比如:

obj.skill = [1, 2, 3];
obj.age.push(4);

控制台输出:
//空

实际上,不止push,包括slice,shift,unshif...都是没有作用.

5. 重写数组的方法

let arr = ['push', 'slice', 'shift', 'unshift'];
arr.forEach(method=> {
 let oldPush = Array.prototype[method];
 Array.prototype[method] = function(value) {
  console.log('数据更新了')
  oldPush.call(this, value)
 }
})

再执行以下代码:

obj.skill = [1, 2, 3];
obj.skill.push(4);

控制台输出:
//数据更新了

但是,数组的length操作仍然是无效的。这也是为什么vue中只能通过方法去改变数组的原因了。

总结: Object.defineProperty只是解决了状态变更后,如何触发通知的问题,那要通知谁呢?谁会关心那些属性发生了变化呢?以后再说。

以下完整代码

let obj = {
 name: 'jw',
 age: {
  old: '60'
 }
}

// vue 数据劫持 Observer.defineProperty

function observer(obj) {
 if(typeof obj === 'object') {
  for (let key in obj) {
   defineReactive(obj, key, obj[key]);
  }
 }
}

function defineReactive(obj, key, value) {
 observer(value);

 Object.defineProperty(obj, key, {
  get() {
   return value;
  },
  set(val) {
   console.log('数据更新了')
   value = val;
  }
 })
}
observer(obj);


// obj.age.old = '50'


// Object.defineProperty 对 数组无效
let arr = ['push', 'slice', 'shift', 'unshift'];

arr.forEach(method=> {
 let oldPush = Array.prototype[method];
 Array.prototype[method] = function(value) {
  console.log('数据更新了')
  oldPush.call(this, value)
 }
})
obj.skill = [1, 2, 3];
obj.skill.push(4);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery text()要注意啦
Oct 30 Javascript
js 函数的执行环境和作用域链的深入解析
Nov 01 Javascript
将CKfinder整合进CKEditor3.0的新方法
Jan 10 Javascript
JavaScript中的property和attribute介绍
Dec 26 Javascript
弹出最简单的模式化遮罩层的js代码
Dec 04 Javascript
Map.vue基于百度地图组件重构笔记分享
Apr 17 Javascript
javascript帧动画(实例讲解)
Sep 02 Javascript
js技巧之十几行的代码实现vue.watch代码
Jun 09 Javascript
使用js实现一个简单的滚动条过程解析
Sep 10 Javascript
Vue使用JSEncrypt实现rsa加密及挂载方法
Feb 07 Javascript
基于javascript原生判断DOM是否加载完毕
Oct 14 Javascript
js加减乘除精确运算方法实例代码
Jan 17 Javascript
vue 对象添加或删除成员时无法实时更新的解决方法
May 01 #Javascript
JavaScript强制类型转换和隐式类型转换操作示例
May 01 #Javascript
Vue源码之关于vm.$delete()/Vue.use()内部原理详解
May 01 #Javascript
Vue.extend实现挂载到实例上的方法
May 01 #Javascript
JS html事件冒泡和事件捕获操作示例
May 01 #Javascript
JS实现的贪吃蛇游戏案例详解
May 01 #Javascript
javascript原型链学习记录之继承实现方式分析
May 01 #Javascript
You might like
WINDOWS 2000下使用ISAPI方式安装PHP
2006/09/05 PHP
解析二进制流接口应用实例 pack、unpack、ord 函数使用方法
2013/06/18 PHP
总结PHP删除字符串最后一个字符的三种方法
2016/08/30 PHP
使用PHP连接数据库_实现用户数据的增删改查的整体操作示例
2017/09/01 PHP
ext combox 下拉框不出现自动提示,自动选中的解决方法
2010/02/24 Javascript
jquery向.ashx文件post中文乱码问题的解决方法
2011/03/28 Javascript
js关闭子窗体刷新父窗体实现方法
2012/12/04 Javascript
nodejs npm包管理的配置方法及常用命令介绍
2014/06/05 NodeJs
JavaScript添加随滚动条滚动窗体的方法
2016/02/23 Javascript
原生JS实现网络彩票投注效果
2016/09/25 Javascript
Angular使用$http.jsonp发送跨站请求的方法
2017/03/16 Javascript
jquery编写日期选择器
2017/03/16 Javascript
Vuex之理解Store的用法
2017/04/19 Javascript
Angularjs按需查询实例代码
2017/10/30 Javascript
web前端页面生成exe可执行文件的方法
2018/02/08 Javascript
vue项目国际化vue-i18n的安装使用教程
2018/03/14 Javascript
微信小程序form表单组件示例代码
2018/07/15 Javascript
详解Vue demo实现商品列表的展示
2019/05/07 Javascript
el-select数据过多懒加载的解决(loadmore)
2019/05/29 Javascript
解决vue单页面修改样式无法覆盖问题
2019/08/05 Javascript
Python批量重命名同一文件夹下文件的方法
2015/05/25 Python
Python 爬虫模拟登陆知乎
2016/09/23 Python
python之Socket网络编程详解
2016/09/29 Python
python中ImageTk.PhotoImage()不显示图片却不报错问题解决
2018/12/06 Python
Python如何计算语句执行时间
2019/11/22 Python
一篇文章带你搞定Ubuntu中打开Pycharm总是卡顿崩溃
2020/11/02 Python
关于python中remove的一些坑小结
2021/01/04 Python
一套中级Java程序员笔试题
2015/01/14 面试题
优质的学校老师推荐信
2013/10/28 职场文书
新闻记者个人求职的自我评价
2013/11/28 职场文书
后进生转化工作制度
2014/01/17 职场文书
财政局个人年终总结
2015/03/03 职场文书
学雷锋献爱心倡议书
2015/04/27 职场文书
经典人生语录分享:不畏将来,不念过去,笑对当下
2019/12/12 职场文书
PostgreSQL13基于流复制搭建后备服务器的方法
2022/01/18 PostgreSQL
Mysql Innodb存储引擎之索引与算法
2022/02/15 MySQL