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 相关文章推荐
JavaScript高级程序设计 阅读笔记(十二) js内置对象Math
Aug 14 Javascript
谈谈我对JavaScript DOM事件的理解
Dec 18 Javascript
ES6记录异步函数的执行时间详解
Aug 31 Javascript
jQuery图片切换动画效果
Feb 28 Javascript
深入理解JavaScript 参数按值传递
May 24 Javascript
Vue0.1的过滤代码如何添加到Vue2.0直接使用
Aug 23 Javascript
详解Vue取消eslint语法限制
Aug 04 Javascript
Vue自定义指令写法与个人理解
Feb 09 Javascript
vue使用自定义指令实现拖拽
Jan 29 Javascript
微信小程序返回上一级页面的实现代码
Jun 19 Javascript
JavaScript实现移动小精灵的案例代码
Dec 12 Javascript
js实现有趣的倒计时效果
Jan 19 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
php实现12306火车票余票查询和价格查询(12306火车票查询)
2014/01/14 PHP
php弹出对话框实现重定向代码
2014/01/23 PHP
php实现mysql数据库操作类分享
2014/02/14 PHP
深入理解php printf() 输出格式化的字符串
2016/05/23 PHP
CI(CodeIgniter)框架实现图片上传的方法
2017/03/24 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
网页前台通过js非法字符过滤代码(骂人的话等等)
2010/05/26 Javascript
jQuery 常见操作实现方式和常用函数方法总结
2011/05/06 Javascript
JavaScript实现的购物车效果可以运用在好多地方
2014/05/09 Javascript
jQuery+ajax实现无刷新级联菜单示例
2015/05/21 Javascript
Javascript监视变量变化的方法
2015/06/09 Javascript
JAVA四种基本排序方法实例总结
2015/07/24 Javascript
jQuery Chosen通用初始化
2017/03/07 Javascript
JS中将多个逗号替换为一个逗号的实现代码
2017/06/23 Javascript
JS实现运动缓冲效果的封装函数示例
2018/02/18 Javascript
微信小程序开发打开另一个小程序的实现方法
2020/05/17 Javascript
使用Taro实现小程序商城的购物车功能模块的实例代码
2020/06/05 Javascript
[46:49]完美世界DOTA2联赛PWL S3 access vs Rebirth 第二场 12.19
2020/12/24 DOTA
Python中的startswith和endswith函数使用实例
2014/08/25 Python
Python3.x中自定义比较函数
2015/04/24 Python
python 采集中文乱码问题的完美解决方法
2016/09/27 Python
Python爬取个人微信朋友信息操作示例
2018/08/03 Python
解决pycharm安装后代码区不能编辑的问题
2018/10/28 Python
Python检查ping终端的方法
2019/01/26 Python
Pandas之MultiIndex对象的示例详解
2019/06/25 Python
Django框架创建mysql连接与使用示例
2019/07/29 Python
学python安装的软件总结
2019/10/12 Python
python中count函数简单的实例讲解
2020/02/06 Python
Python 私有属性和私有方法应用场景分析
2020/06/19 Python
米兰必去买手店排行榜首位:Antonioli
2016/09/11 全球购物
大四学生思想汇报
2014/01/13 职场文书
公益活动邀请函
2014/02/05 职场文书
《画》教学反思
2014/04/14 职场文书
打架检讨书范文
2015/01/27 职场文书
2015年质检工作总结
2015/05/04 职场文书
pytest配置文件pytest.ini的详细使用
2021/04/17 Python