好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ES6+ Proxy

ES6+ Proxy

1. 前言

本节我们将学习 ES6 的新增知识点 ——Proxy,Proxy 是代理的意思。Proxy 是 一个 对象,用于定义基本操作的 自定义 行为(如 属性 查找、赋值、枚举、 函数 调用 等)。这是 MDN 上的定义,但是不容易理解,想要理解 Proxy 我们首先需要知道什么是代理?

在日常开发中比较常见的代理常见有,使用 Charles 代理抓包、 Nginx 服务器的反向代理,以及 VPN 等,都用到了代理。什么是代理呢?我们先看一张图:

上图是客户端访问网络的示意图,客户端不能直接访问网络,它只能先访问代理服务器,只有代理服务器才能有权限访问,然后代理服务器把客户端请求的信息转发给目标服务器,最后代理服务器在接收到目标服务器返回的结果再转发给客户端,这样就完成了整个请求的响应过程。这是现在大多数服务器的架构,我们可以把上图的 Proxy Server 理解为 Nginx 。代理有正向代理和反向代理,有兴趣的小伙伴可以去深入了解一下。

本节说的 Proxy 就是作用在 JavaScript 中的一种代理服务,代理的过程其实就是一种对数据的劫持过程,Proxy 可以对我们定义的对象的 属性 进行劫持,当我们访问或设置 属性 时,会去 调用 对应的钩子执行。在 ES5 中我们曾学习过 Object.define property() 它的作用和 Proxy 是相同的,但是 Object.define property() 存在一些 性能 问题,Proxy 对其进行了 升级 和扩展更加方便和易用。本节我们将学习 Proxy 的使用。

2. Object.define property()

在学习 Proxy 之前,我们先来回归一下 ES5 中的 Object.define property() ,接触过前端框架的同学应该都知道 Vue 和 React,其中 Vue 中的响应式数据底层就是使用 Object.define property() 这个 API 来实现的。下面是 Object.define property() 的语法。

 Object .  defineProperty  ( obj ,  prop ,  descriptor ) 
 

Object.define property() 会接收三个参数:

obj 需要观察的对象; prop 是 obj 上的 属性 名; descriptor 对 prop 属性 的描述。

当我们去观察 一个 对象时需要在 descriptor 中去定义 属性 的描述参数。在 descriptor 对象中提供了 get 和 set 方法 ,当我们访问或设置 属性 值时会触发对应的 函数 。

  var  obj  =   {  }  ; 
 var  value  =  undefined ; 

Object .  defineProperty  ( obj ,   "a"  ,   { 
   get  :   function  (  )   { 
    console .  log  (  'value:'  ,  value ) 
     return  value ; 
   }  , 
   set  :   function  ( newValue )   { 
    console .  log  (  'newValue:'  ,  newValue ) 
    value  =  newValue ; 
   }  , 
  enumerable :   true  , 
  con fig urable :   true 
 }  )  ; 
obj . a ; 	 // value: undefined 
obj . a  =   ; 	 //  newValue: 20 
 

上面的 代码 中,我们使用 一个 变量 value 来保存值,这里需要注意的是,不能直接使用 obj 上的值,否则就会出现死循环。

Object.define property() 是 Vue2 的核心, Vue2 在初始化时会对数据进行劫持,如果劫持的 属性 还是对象的话需要递归劫持。下面我们把 Vue2 中数据劫持的核心 代码 写出来。

  var  data  =   { 
  name :   'imooc'  , 
  lession :   'ES6 Wiki'  , 
  obj :   { 
    a :  
   } 
 } 

 observer  ( data )  ; 


 function   observer  ( data )   { 
   if   (  typeof  data  !==   'object'   ||  data  ==   null  )   { 
     return  ; 
   } 

   const  keys  =  Object .  keys  ( data )  ; 

   for   (  let  i  =   ;  i  <  keys . length ;  i ++  )   { 
     let  key  =  keys [ i ]  ; 
     let  value  =  obj [ key ]  ; 
     defineReactive  ( obj ,  key ,  value )  ; 
   } 
 } 

 function   defineReactive  ( obj ,  key ,  value )   { 
   observer  ( value )  ; 

  Object .  defineProperty  ( obj ,  key ,   { 
     get  (  )   { 
       return  value ; 
     }  , 
     set  ( newValue )   { 
       if   ( newValue  ===  value )   return  ; 
       observer  ( newValue )  ; 
      value  =  newValue ; 
     } 
   }  ) 
 } 
 

上面 代码 的核心是 defineReactive 方法 ,它是递归的核心 函数 ,用于重新定义对象的读写。从上面的 代码 中我们发现 Object.define property() 是有缺陷的,当观察的数据嵌套非常深时,这样是非常耗费 性能 的,这也是为什么现在 Vue 的作者极力推广 Vue3 的原因之一,Vue3 的底层使用了 Proxy 来代替 Object.define property() 那 Proxy 具体有什么好处呢?

3. Proxy

首先我们来看下 Proxy 是如何使用的,语法:

  const  p  =   new   Proxy  ( target ,  handler ) 
 

Proxy 对象是 一个 类,需要通过 new 去实例化 一个 Proxy 对象,它接收的参数比较简单,只有两个:

target:需要使用 Proxy 进行观察的目标对象; handler:对目标对象 属性 进行处理的对象,包含了处理 属性 的回调 函数 等。

  const  handler  =   { 
	 get  :   function  ( obj ,  prop )   { 
     return  obj [ prop ]  ; 
   }  , 
   set  :   function  ( obj ,  prop ,  value )   { 
     return  obj [ prop ]   =  value ; 
   } 
 }  ; 

 const  p  =   new   Proxy  (  {  }  ,  handler )  ; 
p . a  =   ; 

console .  log  ( p . a ,  p . b )  ;        // 1, undefined 
 

对比上面的 Object.define property() API 直观的看 Proxy 做了一些精简,把对象、 属性 和值作为 get 和 set 的参数传入进去,不必考虑死循环的问题了。这是直观的感受。

上面我们使用了 Object.define property() API 简单地实现了 Vue2 的响应式原理,那么 Vue 使用 Proxy 是怎么实现的呢?它带来了哪些好处呢?下面我们看实现源码:

  var  target  =   { 
  name :   'imooc'  , 
  lession :   'ES6 Wiki'  , 
  obj :   { 
    a :  
   } 
 } 
 var  p  =   reactive  ( target )  ; 
console .  log  ( p . name )  ; 		 //  获取 值: imooc 
p . obj . a  =   ; 						 //  获取 值: {a : 1} 
console .  log  ( p . obj . a )  ; 		 //  获取 值: {a : 10} 


 function   reactive  ( target )   { 
   return   createReactiveObject  ( target ) 
 } 

 function   createReactiveObject  ( target )   { 
   // 判断如果不是 一个 对象的话返回 
   if   (  !  isObject  ( target )  )   return  target

   // target观察前的原对象; proxy观察后的对象:observed 
  observed  =   new   Proxy  ( target ,   { 
     get  ( target ,  key ,  receiver )   { 
       const  res  =  target [ key ]  ; 
      console .  log  (  ' 获取 值:'  ,  res ) 
       // t odo : 收集依赖... 
       return   isObject  ( res )   ?   reactive  ( res )   :  res
     }  , 
     set  ( target ,  key ,  value ,  receiver )   { 
      target [ key ]   =  value ; 
     } 
   }  ) 

   return  observed
 } 
 

上面的 代码 是从 Vue3 中摘出来的 reactive 函数 的实现,我们可以直观地看到没有对 target 进行递归循环去创建观察对象。而且,当我们对 obj 下的 a 属性 设置值时,执行 get 函数 ,这是为什么呢?这就是 Proxy 的优点,在对 obj 下 属性 设置值时,首先需要 调用 set 方法 获取 target 下 obj 的值,然后判断 obj 又是 一个 对象再去 调用 reactive 函数 进行观察。这样就不需要递归地去对嵌套数据进行观察了,而是在 获取 值的时候,判断 获取 的值是不是 一个 对象,这样极大地节约了资源。

4. 小结

本节主要通过代理和 Object.define property() API 的学习来理解 ES6 的新增知识点 ——Proxy,并且通过 Vue2 和 Vue3 实现响应式原理来对比 Object.define property() 和 Proxy 的优缺点,从而更深入地理解 Proxy。

查看更多关于ES6+ Proxy的详细内容...

  阅读:34次

上一篇

下一篇

第1节:ES6+ 简介    第2节:ES6 环境配置    第3节:ES6+ let    第4节:ES6+ const    第5节:ES6+ 展开语法    第6节:ES6+ 剩余参数    第7节:ES6+ 解构赋值    第8节:ES6+ 模版字符串    第9节:ES6+ 箭头函数    第10节:ES6+ 数值扩展    第11节:ES6+ isFinite()&isNaN()    第12节:ES6+ Number 对象的方法    第13节:ES6+ Math 对象的扩展    第14节:ES6+ includes()    第15节:ES6+ 字符串的扩展    第16节:ES6+ startsWith()    第17节:ES6+ endsWith()    第18节:ES6+ repeat()    第19节:ES6+ padStart()    第20节:ES6+ padEnd()    第21节:ES6+ trim()    第22节:ES6+ Array.from()    第23节:ES6+ of()    第24节:ES6+ find()和findIndex()    第25节:ES6+ copyWithin()    第26节:ES6+ fill()    第27节:ES6+ isArray()    第28节:ES6+ 对象的扩展    第29节:ES6+ flat()    第30节:ES6+ 可选链操作符    第31节:ES6+ Object.is()    第32节:ES6+ Object.assign()    第33节:ES6+ Object.keys()    第34节:ES6+ Object.values()    第35节:ES6+ Object.entries()    第36节:ES6+ 数据结构扩展    第37节:ES6+ Set    第38节:ES6+ WeakSet    第39节:ES6+ Map    第40节:ES6+ WeakMap    第41节:ES6+ Symbol    第42节:ES6+ for...of    第43节:ES6+ 迭代协议    第44节:ES6+ 实现一个简版的 Promise    第45节:ES6+ Promise 基础    第46节:ES6+ Promise 进阶    第47节:ES6+ Generator 基础    第48节:ES6+ Generator 函数应用    第49节:ES6+ async/await    第50节:ES6+ Class 前置知识    第51节:ES6+ Class    第52节:ES6+ Proxy    第53节:ES6+ Reflect(一)    第54节:ES6+ Reflect(二)    第55节:ES6+ 模块化(一)    第56节:ES6+ 模块化(二)    第57节:ES6实战1-实现Vue3 reactive 源码    第58节:ES6实战2-实现 Vue3 effect 源码    第59节:ES6 实战2-封装请求    第60节:ES6+ 实战3-代码整洁之道    第61节:ES6 Map原理分析    第62节:ES6module语法加载importexport    第63节:ES6的循环与可迭代对象示例详解