ES6入门:数据劫持、Proxy、Reflect
- 什么是数据劫持
- Object数据劫持实现原理
- Array数据劫持的实现原理
- Proxy、Reflect
一、什么是数据劫持
定义:访问或者修改对象的某个属性时,在访问和修改属性值时,除了执行基本的数据获取和修改操作以外,还基于数据的操作行为,以数据为基础去执行额外的操作。
当前最经典的数据劫持应用就是数据渲染,各大前端框架的核心功能都是基于数据渲染来实现。
数据劫持实现的核心API就是在ES5中提供的Object.defineProperty()以及基于数组的数据修改方法push、pop、unshift、shift、slice、sort、reverse。
为什么要使用数据劫持?
在前端页面渲染中,最经典的触发渲染方案必然是基于事件机制实现,这种实现渲染的方案有个很大的闭端就是需要通过事件监听机制触发JS事件,然后JS通过document获取需要重新渲染的DOM,然后在js的DOM模型上修改数据触发document渲染页面。

在浏览器中document只是提供给JS操作文档模型的接口,双方通讯通道资源有限,基于事件机制触发页面渲染会消耗这个这个通道的大量资源,降低浏览器性能,下面来看看基于数据劫持实现数渲染的模型图(JS与document通讯仅仅只需要一次,而且基于虚拟DOM的支持还可以实现最精准的DOM渲染):

案例需求:分别使用Object数据结构和Array数据结构实现div文本基于input输入实现时时渲染。

二、Object数据劫持实现原理
实现Object数据劫持实现页面渲染的核心原理:
- defineProperty(obj,key,{setter,getter}),其原理可以了解这篇博客:初识JavaScript对象
- 需要注意的是在对数据实现数据监听时,需要对Object实现深度绑定(递归)
<input type="text" name="" id="demo">
<div id="show"></div>
<script>
var oDiv = document.getElementById('show');
var oInput = document.getElementById('demo');
var oData = {
valueObj:{
value:'duyi'
},
name:'haha'
}
//输入框事件:触发数据修改(写入)
oInput.oninput = function(){
oData.name = this.value;
// oData.valueObj.value = this.value;
}
//修改DOM数据(页面渲染)
function upDate(){
oDiv.innerText = oData.name;
// oDiv.innerText = oData.valueObj.value;
}
upDate();//初始数据渲染
//给数据绑定监听
function Observer(data){
if(!data || typeof data != 'object'){
return data;
};
// Object.keys(data)不能获取数组的索引,所以Observer无法实现数据数据监听
Object.keys(data).forEach(function(item){
definedRective(data,item,data[item]); //
})
}
//数据监听:当setter被触发时,修改数据并渲染到页面
function definedRective(data,key,val){
Observer(val); //使用递归深度监听对象数据变化,例如:示例数据oData.valueObj.value的监听
Object.defineProperty(data,key,{
get(){
return val;
},
set(newValue){
if(newValue == val) return;
val = newValue;
upDate(); //数据渲染到DOM
}
})
}
Observer(oData);//给数据绑定监听方法
</script>
三、Array数据劫持的实现原理
基于数组的数据修改方法push、pop、unshift、shift、slice、sort、reverse实现数据劫持:
为什么Array的元素修改不能使用Object.defineProperty(arr,key,{...})实现数据劫持呢?这是因为Array的索引不能使用setter和getter数据描述符,所以无法实现,这也就是在Vue中无法使用Array[index]的方式触发数据渲染的原因。
这里的示例我没有实现全部的数组方法的数据劫持,只是用了push一个方法来实现示例需求:
<input type="text" name="" id="demo">
<div id="show"></div>
<script>
var oDiv = document.getElementById('show');
var oInput = document.getElementById('demo');
let arr = ["duyi"];
let {push} = Array.prototype;
console.log(push);
function upArrData(){
oDiv.innerText = arr[arr.length-1];
}
upArrData();
oInput.oninput = function(){
arr.push(this.value);
}
Object.defineProperty(Array.prototype,'push',{
value:(function(){
return function(...arg){
push.apply(arr,arg);
upArrData();
}
})()
});
</script>
四、Proxy、Reflect
关于Proxy、Reflect是什么?能做什么?以及应用场景有哪些?先都放到一边,我们先来看看使用Proxy如何实现前面得案例需求:
<input type="text" id="demo" name="">
<div id="show"></div>
<script>
const oInput = document.getElementById("demo");
const oDiv = document.getElementById("show"); let oData = {
name:"duyi",
valueObj:{
value:"aaa"
}
}
function upData(){
oDiv.innerText = oData.name;
}
upData();
oInput.oninput = function(){
proData.name = this.value;
}
let proData = new Proxy(oData,{
set(target,key,value,receiver){
Reflect.set(target,key,value);
upData();
},
get(target,key,receiver){
return Reflect.get(target,key);
}
});
</script>
通过下面这个示图来了解Proxy、Reflect实现案例需求的流程图:

当oninput事件触发时,proxy代理data接收数据,然后通过reflect映射给data;然后,当有获取Data数据时,Proxy会将get操作拦截下来,再通过reflect映射出data的真实数据。
Proxy在代码量上远远优于Object.defineProperty()的数据劫持操作,并且可以直接作用数组。
Proxy不能直接对引用值属性做深入代理,需要一个节点一个节点的进行创建proxy对象来实现,所以上面的代码如果替换成这样两条与就不能触发数据渲染:
oDiv.innerText = oDiv.valueObj.value; //渲染数据
proData.valueObj.value = this.value; //修改数据
Proxy是什么?
Proxy用于修改某些操作的默认行为,同等于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
Proxy能做什么?
Proxy可以理解在目标对象前架设一个拦截层,外界对该对象的访问都必须先通过这层拦截,因此可以提供一种机制对外界的访问进行过滤和改写。Proxy词意为“代理”,所以通常也被称为代理器。
Proxy的应用场景有?
数据验证、值修正及附加属性、扩展构造函数等,详细可以了解MDN手册:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Reflect是什么?
Reflect词意为“反射”,其对象方法与Proxy对象的方法对应,并且也与Object的方法对应,也就是javaScript用来实现映射的API,注意Reflect不能执行new指令。
详细可以了解阮一峰的ES6手册:http://es6.ruanyifeng.com/?search=reflect&x=0&y=0#docs/reflect
set(target,key,value,receiver){
//value 写入的值
Reflect.set(target,key,value);
upData();
},
get(target,key,receiver){
// target--属性所属的对象
// key--属性的名称
// receiver--代理对象
// console.log(target,key,receiver);
return Reflect.get(target,key);
}
注意Proxy无操作会直接转发代理,可以理解为它内部直接执行了Reflect映射:
let target = {};
let p = new Proxy(target,{});
p.a = 37;
console.log(target.a); //
关于Proxy和Reflect以后再来补充,毕竟这两个API因为不是语法糖,而是浏览器新拓展的全新功能,转码工具无法进行转码,而浏览器的兼容性目前还很糟糕,还不能得到广泛的应用。
ES6入门:数据劫持、Proxy、Reflect的更多相关文章
- ES6入门系列 ----- 使用Proxy 实现观察者模式
观察者模式是指函数自动观察数据对象的变化, 一旦对象有变化,函数就会自动执行. 它定义了一种一对多的依赖关系,我们用Proxy来实现一个简单的观察者模式(PS: 初学我们认为 观察者模式 == 发布订 ...
- ES6走一波 Proxy/Reflect
Proxy:像拦截器,对目标对象修改等进行拦截,是一种元编程(meta programming),即修改JS语言本身. //生成proxy实例,两个参数都是对象,targetObj是要拦截的目标对象, ...
- ES6中的元编程-Proxy & Reflect
前言 ES6已经出来好久了,但是工作中比较常用的只有let const声明,通过箭头函数改this指向,使用promise + async 解决异步编程,还有些数据类型方法...所以单独写一篇文章学习 ...
- es6(11)--Proxy,Reflect
//Proxy,Reflect { let obj={ time:'2018-06-25', name:'net', _r:123 }; let monitor = new Proxy(obj,{ / ...
- ES6入门笔记
ES6入门笔记 02 Let&Const.md 增加了块级作用域. 常量 避免了变量提升 03 变量的解构赋值.md var [a, b, c] = [1, 2, 3]; var [[a,d] ...
- Vue 核心之数据劫持
前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular.Regular.Vue.React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现 ...
- Vue框架核心之数据劫持
本文来自网易云社区. 前瞻 当前前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular.Regular.Vue.React等等,它们最大的优点就是可以实现数据绑定,再也不需 ...
- Vue核心之数据劫持
前瞻 当前前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Anglar,Regular,Vue,React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了, ...
- 对数据劫持 OR 数据代理 的研究------------引用
数据劫持,也叫数据代理. 所谓数据劫持,指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果.比较典型的是 Object.defineProperty() 和 ...
随机推荐
- 性能分析 | 线上CPU100%排查
不知道在大家面试中,有没有遇到这个问题: 生产服务器上部署了几个java程序,突然出现了CPU100%的异常告警,你如何定位出问题呢? 这个问题分为两版回答! 高调版 对不起,我是做研发的,这个问题在 ...
- tensorflow查看ckpt各节点名称
from tensorflow.python import pywrap_tensorflowimport os checkpoint_path=os.path.join('output/res101 ...
- Redis有序Set、无序Set的使用经历
为了实现一个类似关系数据库中的卖家信息的单表,首先我们知道单表必然可增删查改,其次为了区分先来后到又需要有ID主键且自增长.开始考虑使用hash数据类型,因为hash是key+列1.列2...这样一来 ...
- 查看进程的命令ps
查看进程的命令:ps aux strace -p pid(进程id) 杀死进程:kill pid(进程id)强制杀死进程:kill -9 pid(进程id) linux ps 命令查看进程状态linu ...
- mysql一条语句实现插入或更新的操作
,),(,) ON DUPLICATE KEY UPDATE c=VALUES(c); 或者 INSERT INTO table (id,a,b,c) select id,a,b,c from xxx ...
- InfluxDB入门教程
前言InfluxDB是一个时序性数据库,详细资料如下http://liubin.org/blog/2016/02/18/tsdb-intro/ 下载和安装LZ从官网下载的是influxdb-1.2.4 ...
- webdriver(chrome无头浏览器)
'''chrome无头浏览器''' from selenium.webdriver.chrome.options import Options # 导入相应的类 from selenium impor ...
- 搭建IIS CA DC Exchange TMG SQL (CA DC篇)
搭建IIS CA DC Exchange TMG SQL (CA DC篇) 步骤 1: 在“下一步(N) > (按下按钮)”(位于“添加角色向导”中)上用户左键单击 步骤 2: 在“Ac ...
- 齐治堡垒机前台远程命令执行漏洞(CNVD-2019-20835)分析
一.基本信息 漏洞公告:https://www.cnvd.org.cn/flaw/show/1632201 补丁信息:该漏洞的修复补丁已发布,如果客户尚未修复该补丁,可联系齐治科技的技术支持人员获得具 ...
- 使用webpack搭建一个多页应用
一.前言 最近需要为公司的活动写8个左右的移动端分享页面,有比较多的页面是公用的,如果用传统的方式来写的话,对于公用的代码抽取,css代码的压缩都是比较麻烦的,所以选择了webpack来搭建一个基本 ...