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实现快速排序(自已编写)
Dec 19 Javascript
javascript实现类似超链接的效果
Dec 26 Javascript
Node.js如何自动审核团队的代码
Jul 20 Javascript
再谈javascript常见错误及解决方法
Sep 16 Javascript
JQuery和HTML5 Canvas实现弹幕效果
Jan 04 Javascript
js实现动态显示时间效果
Mar 06 Javascript
详解node.js平台下Express的session与cookie模块包的配置
Apr 26 Javascript
详解使用nvm管理多版本node的方法
Aug 30 Javascript
vue cli3.0 引入eslint 结合vscode使用
May 27 Javascript
layui点击左侧导航栏,实现不刷新整个页面,只刷新局部的方法
Sep 25 Javascript
9种方法优化jQuery代码详解
Feb 04 jQuery
ant design vue中表格指定格式渲染方式
Oct 28 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将数据导入到Foxmail
2006/10/09 PHP
简单的用PHP编写的导航条程序
2006/10/09 PHP
php注册审核重点解析(数据访问)
2017/05/23 PHP
PHP实现长轮询消息实时推送功能代码实例讲解
2021/02/26 PHP
Gambit vs CL BO3 第一场 2.13
2021/03/10 DOTA
在模板页面的js使用办法
2010/04/01 Javascript
不要在cookie中使用特殊字符的原因分析
2010/07/13 Javascript
JS.elementGetStyle(element, style)应用示例
2013/09/24 Javascript
运用JQuery的toggle实现网页加载完成自动弹窗
2014/03/18 Javascript
JS实现闪动的title消息提醒效果
2014/06/20 Javascript
jquery实现触发时更新下拉列表内容的方法
2015/12/02 Javascript
jquery采用oop模式class类的使用示例
2016/01/22 Javascript
JS取模、取商及取整运算方法示例
2016/10/13 Javascript
JS 实现Base64编码与解码实例详解
2016/11/07 Javascript
jQuery插件FusionWidgets实现的Cylinder图效果示例【附demo源码】
2017/03/23 jQuery
jQuery.Ajax()的data参数类型详解
2017/07/23 jQuery
微信小程序表单弹窗实例
2018/07/19 Javascript
React实现全局组件的Toast轻提示效果
2018/09/21 Javascript
微信小程序实现图片滚动效果示例
2018/12/05 Javascript
微信小程序-可移动菜单的实现过程详解
2019/06/24 Javascript
JS hasOwnProperty()方法检测一个属性是否是对象的自有属性的方法
2021/01/29 Javascript
Python 的描述符 descriptor详解
2016/02/27 Python
Python3数字求和的实例
2019/02/19 Python
Python常用数据类型之间的转换总结
2019/09/06 Python
python:批量统计xml中各类目标的数量案例
2020/03/10 Python
使用python从三个角度解决josephus问题的方法
2020/03/27 Python
浅谈django 模型类使用save()方法的好处与注意事项
2020/03/28 Python
浅谈matplotlib默认字体设置探索
2021/02/03 Python
HTML5之SVG 2D入门7—SVG元素的重用与引用
2013/01/30 HTML / CSS
华为python面试题
2016/05/03 面试题
方正Java笔试题
2014/07/03 面试题
2015年元旦文艺汇演主持词
2014/03/26 职场文书
小组口号大全
2014/06/09 职场文书
婚礼新人答谢词
2015/01/04 职场文书
在职证明书模板
2015/06/15 职场文书
新课程改革心得体会
2016/01/22 职场文书