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中的cacheStorage使用详解
Jul 29 Javascript
jQuery点击头像上传并预览图片
Feb 23 Javascript
Layui table 组件的使用之初始化加载数据、数据刷新表格、传参数
Sep 11 Javascript
JS实现合并json对象的方法
Oct 10 Javascript
详解KOA2如何手写中间件(装饰器模式)
Oct 11 Javascript
vue-quill-editor+plupload富文本编辑器实例详解
Oct 19 Javascript
总结4个方面优化Vue项目
Feb 11 Javascript
js类的继承定义与用法分析
Jun 21 Javascript
Laravel admin实现消息提醒、播放音频功能
Jul 10 Javascript
js 对象使用的小技巧实例分析
Nov 08 Javascript
JS创建或填充任意长度数组的小技巧汇总
Oct 24 Javascript
vue里使用create, mounted调用方法
Apr 26 Vue.js
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观察者模式示例【Laravel框架中有用到】
2018/06/15 PHP
laravel model模型定义实现开启自动管理时间created_at,updated_at
2019/10/17 PHP
PHP与Web页面的交互示例详解一
2020/08/04 PHP
用JavaScript和注册表脚本实现右键收藏Web页选中文本
2007/01/28 Javascript
ajax 文件上传应用简单实现
2009/03/03 Javascript
js 兼容多浏览器的回车和鼠标焦点事件代码(IE6/7/8,firefox,chrome)
2010/04/14 Javascript
jquery animate图片模向滑动示例代码
2011/01/26 Javascript
js浏览器本地存储store.js介绍及应用
2014/05/13 Javascript
JavaScript交换两个变量值的七种解决方案
2016/12/01 Javascript
ionic环境配置及问题详解
2017/06/27 Javascript
electron demo项目npm install安装失败的解决方法
2018/02/06 Javascript
原生js调用json方法总结
2018/02/22 Javascript
Element UI 自定义正则表达式验证方法
2018/09/04 Javascript
layui prompt 设置允许空白提交的方法
2019/09/24 Javascript
vue实现配置全局访问路径头(axios)
2019/11/01 Javascript
UEditor 自定义图片视频尺寸校验功能的实现代码
2020/10/20 Javascript
vue前端和Django后端如何查询一定时间段内的数据
2021/02/28 Vue.js
[05:42]DOTA2英雄梦之声_第10期_蝙蝠骑士
2014/06/21 DOTA
qpython3 读取安卓lastpass Cookies
2016/06/19 Python
对Python中for复合语句的使用示例讲解
2018/11/01 Python
浅析python3字符串格式化format()函数的简单用法
2018/12/07 Python
python判断计算机是否有网络连接的实例
2018/12/15 Python
Python函数中不定长参数的写法
2019/02/13 Python
pandas 如何分割字符的实现方法
2019/07/29 Python
twilio python自动拨打电话,播放自定义mp3音频的方法
2019/08/08 Python
python 链接sqlserver 写接口实例
2020/03/11 Python
Python-for循环的内部机制
2020/06/12 Python
Python 如何展开嵌套的序列
2020/08/01 Python
python 监控服务器是否有人远程登录(详细思路+代码)
2020/12/18 Python
退休感言
2014/01/28 职场文书
薪酬专员岗位职责
2014/02/18 职场文书
村党支部换届选举方案
2014/05/02 职场文书
体检通知范文
2015/04/21 职场文书
MySQL 分页查询的优化技巧
2021/05/12 MySQL
Go语言设计模式之结构型模式
2021/06/22 Golang
如何用六步教会你使用python爬虫爬取数据
2022/04/06 Python