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 相关文章推荐
超级兔子让浮动层消失的前因后果
Mar 09 Javascript
ie支持function.bind()方法实现代码
Dec 27 Javascript
js实现简洁的滑动门菜单(选项卡)效果代码
Sep 04 Javascript
jQuery实现鼠标滑过链接控制图片的滑动展开与隐藏效果
Oct 28 Javascript
举例说明如何为JavaScript的方法参数设置默认值
Nov 17 Javascript
通用javascript代码判断版本号是否在版本范围之间
Nov 29 Javascript
ES6中的箭头函数实例详解
Apr 06 Javascript
Angular 2父子组件数据传递之@Input和@Output详解(下)
Jul 05 Javascript
解决vuejs 使用value in list 循环遍历数组出现警告的问题
Sep 26 Javascript
详解Nuxt内导航栏的两种实现方式
Apr 16 Javascript
vue-video-player 断点续播的实现
Feb 01 Vue.js
vue实现锚点定位功能
Jun 29 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
PHP5中MVC结构学习
2006/10/09 PHP
编写安全 PHP应用程序的七个习惯深入分析
2013/06/08 PHP
完善CodeIgniter在IDE中代码提示功能的方法
2014/07/19 PHP
php 删除指定文件夹的实例讲解
2017/07/25 PHP
TP5框架实现签到功能的方法分析
2020/04/05 PHP
Chrome中JSON.parse的特殊实现
2011/01/12 Javascript
jquery获取被勾选的checked(选中)的那一行的3列和4列的值
2013/07/04 Javascript
js 操作select与option(示例讲解)
2013/12/20 Javascript
ExtJS4给Combobox设置列表中的默认值示例
2014/05/02 Javascript
jQuery切换所有复选框选中状态的方法
2015/07/02 Javascript
Bootstrap按钮下拉菜单组件详解
2016/05/10 Javascript
JavaScript拖动层Div代码
2017/03/01 Javascript
基于Vue实现timepicker
2017/04/25 Javascript
Vue2.x中的父子组件相互通信的实现方法
2017/05/02 Javascript
CSS3+JavaScript实现翻页幻灯片效果
2017/06/28 Javascript
JS中判断某个字符串是否包含另一个字符串的五种方法
2018/05/03 Javascript
vue脚手架搭建过程图解
2018/06/06 Javascript
原生JS实现动态加载js文件并在加载成功后执行回调函数的方法
2020/12/30 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
javascript设计模式 ? 抽象工厂模式原理与应用实例分析
2020/04/09 Javascript
Python中利用aiohttp制作异步爬虫及简单应用
2018/11/29 Python
python中可以声明变量类型吗
2020/06/18 Python
Django多个app urls配置代码实例
2020/11/26 Python
HTML5 window/iframe跨域传递消息 API介绍
2013/08/26 HTML / CSS
实例教程 HTML5 Canvas 超炫酷烟花绽放动画实现代码
2014/11/05 HTML / CSS
Original Penguin英国官方网站:美国著名休闲时装品牌
2016/10/30 全球购物
网上常见的一份Linux面试题(多项选择部分)
2015/02/07 面试题
应征英语教师求职信
2013/11/27 职场文书
英语专业职业生涯规划范文
2014/03/05 职场文书
精彩广告词大全
2014/03/19 职场文书
大学毕业寄语大全
2014/04/10 职场文书
党员公开承诺书内容
2014/05/20 职场文书
2014年国庆节寄语
2014/09/19 职场文书
社会实践活动总结
2015/02/05 职场文书
学校重阳节活动总结
2015/03/24 职场文书
2019求职信:应届生求职信范文
2019/04/24 职场文书