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编程语言的编码规范
Oct 21 Javascript
React.js入门学习第一篇
Mar 30 Javascript
14 个折磨人的 JavaScript 面试题
Aug 08 Javascript
js获取页面引用的css样式表中的属性值方法(推荐)
Aug 19 Javascript
浅析ES6的八进制与二进制整数字面量
Aug 30 Javascript
Javascrip实现文字跳动特效
Nov 27 Javascript
浅析JS中什么是自定义react数据验证组件
Oct 19 Javascript
js/jQuery实现全选效果
Jun 17 jQuery
Vue最新防抖方案(必看篇)
Oct 30 Javascript
微信小程序点击按钮动态切换input的disabled禁用/启用状态功能
Mar 07 Javascript
微信小程序实现页面监听自定义组件的触发事件
Nov 01 Javascript
原生js 实现表单验证功能
Feb 08 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中time()和mktime()方法的区别
2013/09/28 PHP
ThinkPHP 3.2 数据分页代码分享
2014/10/14 PHP
PHP将session信息存储到数据库的类实例
2015/03/04 PHP
php mailer类调用远程SMTP服务器发送邮件实现方法
2016/03/04 PHP
php使用preg_match()函数验证ip地址的方法
2017/01/07 PHP
thinkphp5.1框架容器与依赖注入实例分析
2019/07/23 PHP
PHP实现与java 通信的插件使用教程
2019/08/11 PHP
日期函数扩展类Ver0.1.1
2006/09/07 Javascript
JavaScript 学习笔记(十六) js事件
2010/02/01 Javascript
JavaScript学习点滴 call、apply的区别
2010/10/22 Javascript
juqery 学习之三 选择器 层级 基本
2010/11/25 Javascript
JavaScript使用循环和分割来替换和删除元素实例
2014/10/13 Javascript
javascript解析xml实现省市县三级联动的方法
2015/07/25 Javascript
Node.js 应用跑得更快 10 个技巧
2016/04/03 Javascript
jQuery EasyUI 入门必看
2016/06/03 Javascript
基于AngularJs select绑定数字类型的问题
2018/10/08 Javascript
使用VUE实现在table中文字信息超过5个隐藏鼠标移到时弹窗显示全部
2019/09/16 Javascript
JS实现“全选”和"全不选"功能代码实例
2020/02/06 Javascript
[01:07:13]TNC vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
[46:50]Liquid vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python实现的Kmeans++算法实例
2014/04/26 Python
Django读取Mysql数据并显示在前端的实例
2018/05/27 Python
Django中反向生成models.py的实例讲解
2018/05/30 Python
Python WEB应用部署的实现方法
2019/01/02 Python
详解Django项目中模板标签及模板的继承与引用(网站中快速布置广告)
2019/03/27 Python
Cython编译python为so 代码加密示例
2019/12/23 Python
Python可以实现栈的结构吗
2020/05/27 Python
Python如何实现定时器功能
2020/05/28 Python
python 决策树算法的实现
2020/10/09 Python
Spartoo瑞典:鞋子、包包和衣服
2018/09/15 全球购物
eDreams意大利:南欧领先的在线旅行社
2018/11/23 全球购物
英国邮购活的植物主要供应商:Gardening Direct
2019/01/28 全球购物
天网工程实施方案
2014/03/26 职场文书
小学五年级学生评语
2014/04/22 职场文书
党风廉政承诺书2016
2016/03/25 职场文书
2020优秀员工演讲稿(三篇)
2019/10/17 职场文书