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 学习6 操纵元素显示效果的函数
Feb 07 Javascript
基于JQuery的asp.net树实现代码
Nov 30 Javascript
js 走马灯简单实例
Nov 21 Javascript
Javascript Object 对象学习笔记
Dec 17 Javascript
jQuery实现的指纹扫描效果实例(附演示与demo源码下载)
Jan 26 Javascript
AngularJS监听路由的变化示例代码
Sep 23 Javascript
Bootstrap模态框案例解析
Mar 05 Javascript
JavaScript脚本语言是什么_动力节点Java学院整理
Jun 26 Javascript
用vue的双向绑定简单实现一个todo-list的示例代码
Aug 03 Javascript
解决Vue项目打包后打开index.html页面显示空白以及图片路径错误的问题
Oct 25 Javascript
JavaScript进阶(三)闭包原理与用法详解
May 09 Javascript
Vue router配置与使用分析讲解
Dec 24 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&java(二)
2006/10/09 PHP
PHP新手上路(三)
2006/10/09 PHP
PHP移动文件指针ftell()、fseek()、rewind()函数总结
2014/11/18 PHP
iis 7下安装laravel 5.4环境的方法教程
2017/06/14 PHP
OAuth认证协议中的HMACSHA1加密算法(实例)
2017/10/25 PHP
PHP封装的分页类与简单用法示例
2019/02/25 PHP
JQuery插件Style定制化方法的分析与比较
2012/05/03 Javascript
Bootstrap每天必学之基础排版
2015/11/20 Javascript
微信小程序 开发工具快捷键整理
2016/10/31 Javascript
详解JavaScript时间处理之几个月前或几个月后的指定日期
2016/12/21 Javascript
jQuery点击头像上传并预览图片
2017/02/23 Javascript
Extjs表单输入框异步校验的插件实现方法
2017/03/20 Javascript
为JQuery EasyUI 表单组件增加焦点切换功能的方法
2017/04/13 jQuery
JS中使用gulp实现压缩文件及浏览器热加载功能
2017/07/12 Javascript
NodeJS父进程与子进程资源共享原理与实现方法
2018/03/16 NodeJs
jQuery中$原理实例分析
2018/08/13 jQuery
vue使用nprogress加载路由进度条的方法
2020/06/04 Javascript
python实现360皮肤按钮控件示例
2014/02/21 Python
CentOS 6.5中安装Python 3.6.2的方法步骤
2017/12/03 Python
python实现k-means聚类算法
2018/02/23 Python
解决TensorFlow模型恢复报错的问题
2020/02/06 Python
windows+vscode安装paddleOCR运行环境的步骤
2020/11/11 Python
python基于Kivy写一个图形桌面时钟程序
2021/01/28 Python
表达自我的市场:Society6
2018/08/01 全球购物
澳大利亚领先的折扣药房:Chemist Direct(有中文站)
2018/11/24 全球购物
巴西化妆品商店:Lojas Rede
2019/07/26 全球购物
如果重写了对象的equals()方法,需要考虑什么
2014/11/02 面试题
物业经理求职自我评价
2013/09/22 职场文书
会计辞职信范文
2014/01/15 职场文书
就业自我评价
2014/02/04 职场文书
计算机学生的自我评价分享
2014/02/18 职场文书
经典演讲稿汇总
2014/05/19 职场文书
淮阳太昊陵导游词
2015/02/10 职场文书
2015年档案管理员工作总结
2015/05/13 职场文书
晶体管来复再生式二管收音机
2021/04/22 无线电
如何使用Python提取Chrome浏览器保存的密码
2021/06/09 Python