文章目录

  • 数据劫持
    • 为什么在data中定义的数据会自动携带get、set属性方法呢?
      • 数据劫持
        • data与_data
      • Object.DefineProperty
        • Object.DefineProperty的缺点
      • Proxy
      • 总结Proxy和Object.defineProperty的区别

数据劫持

   上一次面试中,考官有问我对Vue当中双向绑定(v-model)的理解,我当时回答的好像很片面,只是解释了这个内置指令的含义,当时面试官听完我的回答,也并没有对我的回答做出任何反应。我就明白了这应该是我的回答可能是错的或者理解的不够深刻。于是我一有空,就对Vue当中的双向绑定进行了更深刻的钻研。

v-model理解

  • v-model是Vue的内置指令,本质上是一种语法糖。主要是负责监听用户的输入事件来更新数据
  • v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定它会根据控件类型自动选取正确的方法来更新元素。
  • v-model本质上还是单向数据流,通过v-bind绑定value值,此时只能通过在data中修改输入框的显示值,即数据只能从data中流向视图,而不能获取用户输入的数据从而修改data中的值;此时需要绑定一个@input事件来实现获取用户输入的数据从而修改data中的值。从而实现数据的双向绑定:数据向下,事件向上

v-model其实是简化了两个操作:
   1.v-bind绑定value属性的值;

   2.v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

<input type="text" v-model="textDetail">
<!--等价于-->
<input type="text" :value="textDetail" @input="textDetail=$event.target.value">

   此时我对data中定义的textDetail挺好奇的:该属性是如何绑定到页面中的?该属性是如何进行一次次修改的?我通过打印输出该数据得到:
   大概意思是在获取data数据至页面应该是调用了get方法,修改该属性应该是调用了set方法。那么问题来了!!!

为什么在data中定义的数据会自动携带get、set属性方法呢?

  • 其实此刻正文才刚刚开始

数据劫持

数据劫持,其实就是数据代理
指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。

data与_data

我们通过全局输出this可以得到:

将data展开得到:

   _data中的数据与data中的数据一致,只不过多了对数据的get和set方法还有一个Observer对象,Vue就是通过_data来实现对data的数据劫持,通过调用_data中的get和set方法去修改和展现数据。

__ob__: Observer对象是vue这个框架对数据设置的监控器,监控数据的变化,一般都是不可枚举的。

原来是_data上的数据携带get和set方法,那问题还是没解决,_data上存在get和set方法是怎么来的?

Object.DefineProperty

   Vue2中是通过Object.DefineProperty实现数据劫持,作用是往data中的一个对象中添加属性,get()和set()方法就是通过Object.DefineProperty添加的,从而实现当外部想要修改data中的值。

Object.DefineProperty使用规范为

  • Object.defineProperty(对象,要添加得键,{配置对象})

配置对象的主要属性有:

  • value:20 //添加的属性的value
  • enumerable:true //是否可以被枚举获取到 默认:false
  • writeable:true //value是否可以被修改 默认:false
  • configurable:true //是否可以被删除 默认:false
  • get(){} //当这个属性被获取的时候调用
  • set(){} //当这个属性被修改的时候调用

Object.DefineProperty的实际应用

const user={name:"jack",}var name=user.name;Object.defineProperty(user, "name", {enumerable: true,writeable: true,configurable: true,get() { return name}, set(e) { name=e} })

可以在浏览器控制台中输出name以及修改name:

Object.DefineProperty就是这样把data中的数据全部加工了一遍,添加上了get和set方法,从而实现了数据的劫持。

Object.DefineProperty的缺点

1.**对象上定义新属性时,Object.defineProperty监听不到。**因为Object.DefineProperty只能对对象的单个属性劫持,如果要对对象进行劫持,需要遍历。

     const user = {name: "jack",}var name = user.name;Object.defineProperty(user, "name", {enumerable: true,writeable: true,configurable: true,get() {return name},set(e) {name = e}})user.age = 20;  // 并没有触发setconsole.log(user);delete user.age;  //并没有触发setconsole.log(user);

2.Object.defineProperty无法监控到数组下标的变化

     let arr = [1, 2, 3]let obj = {}//把arr作为obj的属性监听Object.defineProperty(obj, 'arr', {get() {console.log('触发了get')return arr},set(newVal) {console.log('触发了set', newVal)arr = newVal}})console.log(obj.arr) //触发了get [1,2,3] obj.arr = [1, 2, 3, 4] //触发了set [1,2,3,4] obj.arr.push(5) //触发了getobj.arr.unshift() //触发了getobj.arr.pop() // //触发了getobj.arr.shift() //触发了get
  • 只要不是重新赋值一个新的数组对象,任何对数组内部的修改都不会触发set方法的执行。

3.**object.defineProperty 只能劫持对象的属性。**从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。

const data = {name: 'jack',age: 20,children: {name: 'john'}}
function observer(target) {if (target === null || typeof target !== 'object') {return target}for (let key in target) {//target[key]作为valuelet value = target[key]observer(value)//调用自身,监控内部对象,如果不加遍历不到嵌套对象中的数据,无法修改Object.defineProperty(target, key, {get() {console.log('Object.defineProperty触发了get');return value},set(newValue) {console.log('Object.defineProperty触发了set');if (newValue !== value) {value = newValue//updateView()}}})}}observer(data)data.children.name = 'marray'//此时会调用get和set方法修改数据

Proxy

   在Vue3中则是使用Proxy来进行数据劫持,Proxy不同于Object.defineProperty的是,它是对整个数据对象进行数据劫持,而Object.defineProperty是对数据对象的某个属性进行数据劫持(如果是多层需要循环绑定)。

   Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。IE不兼容。基本格式为:

  • const p = new Proxy(target, handler)

参数:
   target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
   handler: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器(trap):

  • handler.getPrototypeOf()
  • handler.setPrototypeOf()
  • handler.isExtensible()
  • handler.preventExtensions()
  • handler.getOwnPropertyDescriptor()
  • handler.defineProperty()
  • handler.has()//in 操作符的捕捉器。
  • handler.get(target, property)
  • handler.set(target, property, value)
  • handler.deleteProperty()//delete 操作符的捕捉器。
  • handler.ownKeys()
  • handler.apply()
  • handler.construct()//new 操作符的捕捉器。

Proxy的应用——监控数组下标变化

let arr=[1,2,3]let handler={get(target, key, receiver) {console.log('get的key为 ===>' + key);return Reflect.get(target, key, receiver);},set(target, key, value, receiver){console.log('set的key为 ===>' + key, value);return Reflect.set(target, key, value, receiver);}}let p=new Proxy(arr,handler);console.log(p[0]);//get的key为 ===>0//1console.log(p.push(4));//get的key为 ===>push//get的key为 ===>length//set的key为 ===>3 4//set的key为 ===>length 4//4console.log(p);//Proxy {0: 1, 1: 2, 2: 3, 3: 4}console.log(p.shift());//get的key为 ===>shift// get的key为 ===>length// get的key为 ===>0// get的key为 ===>1// set的key为 ===>0 2// get的key为 ===>2// set的key为 ===>1 3// get的key为 ===>3// set的key为 ===>2 4// set的key为 ===>length 3// 1console.log(p);//Proxy {0: 2, 1: 3, 2: 4}reflect也是es6的语法,再proxy使用中常用到reflect,reflect有14中方法,
对应proxy是一样的,但是这俩其实没关系,只是搭配着使用,proxy用来拦截,reflect用来操作。

首先我们可以看到Proxy可以监控到数组下标的变化。
在使用push时先将push的值添加到数组中,再更新数组长度,所以才会有两次get和set。
在使用shift时首先要获取到要删除的元素下标,并删除,再更新每个下标的元素,再更新数组长度。

Proxy的应用——监控对象属性的变化

const user = {name: "jack",}let handler={get(target, key, receiver) {console.log('get的key为 ===>' + key);return Reflect.get(target, key, receiver);},set(target, key, value, receiver){console.log('set的key为 ===>' + key, value);return Reflect.set(target, key, value, receiver);}}let p=new Proxy(user,handler);console.log(p.age=10);// set的key为 ===>age 10// 10console.log(p);//Proxy {name: 'jack', age: 10}

Proxy的应用——监控嵌套对象的数据变化

let obj={name: "jack",phone:{main:10086,else:110}};let handler={get:function(obj,prop){console.log('proxy触发了get');const v = Reflect.get(obj,prop);if(v !== null && typeof v === 'object'){return new Proxy(v,handler);//代理内层}else{return v; // 返回obj[prop]}},set(obj,prop,value){console.log('proxy触发了set');return Reflect.set(obj,prop,value);//设置成功返回true}};let p=new Proxy(obj,handler);console.log(p.name);//会触发get方法//proxy触发了get//jackconsole.log(p.phone.main);//会先触发get方法获取p.b,然后触发返回的新代理对象的.c的set。//proxy触发了get//proxy触发了get//10086

   由此我们可以看到如果我们想要获取到内部N层的数据,proxy就会调用N次get。且proxy在实现监控嵌套对象仅需要一个递归即可实现。

总结Proxy和Object.defineProperty的区别

1.Proxy性能优于Object.defineProperty。 Proxy代理的是整个对象Object.defineProperty只代理对象上的某个属性,如果是多层嵌套的数据需要循环递归绑定;

2.对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到。

3.数组的某些方法(push、unshift和splice)Object.defineProperty监听不到,Proxy可以监听到。

4.Proxy在ie浏览器存在兼容性问题

【Vue知识点】——Vue2和Vue3的数据劫持相关推荐

  1. Vue2 MVVM 双向绑定(数据劫持+发布者-订阅者模式)

    参考文献:https://www.cnblogs.com/libin-1/p/6893712.html https://juejin.im/post/5b2f0769e51d45589f46949e ...

  2. Vue基础——VueJS是什么、Vue的优缺点、vue2和vue3的模板区别、MVVM数据双向绑定、Vue的安装和使用、Vue模板语法-文本渲染、常用的vue的指令

    目录 一.VueJS是什么? 二.Vue的优缺点 三.MVVM 数据双向绑定 四.Vue的安装和使用 五.Vue模板语法-文本渲染 六.常用的vue的指令 一.VueJS是什么? 它是一个轻量级MVV ...

  3. 前端面试题 HTML5 CSS3(盒子模型、盒子水平垂直居中、经典布局) JS(闭包、深浅克隆、数据劫持和拦截) 算法(排序、去重、数组扁平化) Vue(双向数据绑定原理、通信方式)

    前端面试题 HTML5 相关面试题 CSS3 相关面试题 盒子模型 盒子水平垂直居中的方案 经典布局方案 圣杯布局 双飞翼布局 flex布局 定位方式布局 css实现三角形 JS 相关面试题 8种数据 ...

  4. 【Vue学习笔记】尚硅谷Vue2.0+Vue3.0全套教程丨vue.js从入门到精通

    尚硅谷Vue2.0+Vue3.0全套教程丨vue.js从入门到精通 1.Vue核心部分 1.1 Vue简介 1.1.1 Vue是什么? Vue是一套用于构建用户界面的渐进式JavaScript框架. ...

  5. 覆盖vue3.0的最全Vue知识点

    声明:本篇文章纯属笔记性文章,非整体原创,是对vue知识的整理,对自己有很大帮助才分享出来,参考文章传送:1.童欧巴对vue知识的整理 2.我是你的超级英雄对vue知识的整理 3.vue官网 基 础 ...

  6. Vue基础+vue2+vue3 大合集笔记

    系列文章目录 之前没跟对up主,vue基础没打好:跟着尚硅谷从头第二次学Vue,收获了很多并且记下来 万字笔记,平常开发用的多的都在这了 如果有出错的地方请多多指教! 文章目录 系列文章目录 vue概 ...

  7. vue2.0,vue3.0 v-model数据双向绑定

    vue2.0,vue3.0 v-model数据双向绑定 vue.2.0 vue2.0 vue-property-decorator vue3.0 vue.2.0 <base-checkbox v ...

  8. 【Vue】Vue2知识点总结

    Vue知识点总结 Vue的基础概念 Vue是什么 什么是渐进式和框架 什么是MVVM框架 脚手架的使用 全局安装脚手架/包 查看版本 创建项目 进入项目根目录 运行项目 style中开启less功能 ...

  9. 持续不断更新中... 自己整理的一些前端知识点以及前端面试题,包括vue2,vue3,js,ts,css,微信小程序等

    Vue3自考题 1,如何使用vue3的组合式api 答: 在普通的前端项目工程中,在script标签中增加setup即可使用api 使用setup()钩子函数 2,computed 与各个watch之 ...

最新文章

  1. 由于市场判断失误 希捷降低收入预期
  2. 产品经理跪求程序员修改需求
  3. php和python对比-PHP和Python性能比较:放弃PHP改用Python
  4. 集合之四:List接口
  5. javascript技巧收集(200多个)---转
  6. 有时候还逃课的视频转换
  7. c++程序设计中多态与虚函数知识点
  8. python安装jupyterlab_【python】jupyter lab安装与配置
  9. Silverlight/Windows8/WPF/WP7/HTML5周学习导读(8月5日-8月12日)
  10. mongodb索引 多健索引
  11. 各个地图经纬度转换工具类
  12. 网易公开课“Programming Paradigms” 笔记
  13. 原型工具Axure:学习路线及资源
  14. 推荐个不错的 Word 全文翻译和压缩工具!
  15. 两条命令让你的git轻松自动变基,学到了!
  16. 基于 FCCA 的多特征融合的检索方法
  17. PS制作gif动态图
  18. 试用HBuilder编辑H5移动开发
  19. Mac 打开safari浏览器直接卡死解决方法,解决Safari浏览器访问网页卡死重新再打开浏览器还是卡死实例演示
  20. SQL语句查询所有表和查询表里的所有字段

热门文章

  1. 计算机检查磁盘,教你win7系统电脑检测到磁盘错误的解决教程
  2. 脑神经科学简单介绍(小白入门)
  3. 达梦数据库(DM8)基本使用
  4. 利用LORA无线信号接入数据的手持PDA装置
  5. Arduino初初教程8——模拟量输出
  6. GNU ARM汇编--(二十)总结
  7. PyQt5最全26 绘图之drawPoint用像素点绘制正弦曲线
  8. 计算机屏幕出现蓝色条,我的电脑屏幕中间为何会有一道蓝色线条
  9. 解决Windows安装MySQL时出现msvcr120.dll文件丢失问题(完美解决)
  10. 迁移学习 transfer learning