理解JavaScript中的Proxy 与 Reflection API


Posted in Javascript onSeptember 21, 2020

一、创建 Proxy

let target = {}
let proxy = new Proxy(target, {})

proxy.name = "proxy"
console.log(proxy.name)  // proxy
console.log(target.name) // proxy

target.name = "target"
console.log(proxy.name)  // target
console.log(target.name) // target

在上面的例子中,由 Proxy 构造器创建的 proxy 对象会将自身的所有操作直接转发给 target
proxy.name 被赋值为 "proxy" 时,target 对象也会创建 name 属性并获得同样的值。实际上 proxy 对象本身并不创建和存储 name 属性,它只是转发对应的操作给 target

类似的,proxy.name target.name 的值始终保持一致,因为它们实际上都指向了 target.name。这也意味着给 target.name 赋予一个新的值时,该变化也会反映到 proxy.name 上。

使用 set Trap 验证属性

Proxy 允许开发者主动拦截本该转发给 target 对象的底层操作,这些拦截行为通过 trap 实现。每个 trap 都可以覆盖 JavaScript 对象的某些内置行为,即 proxy 允许通过 trap 拦截并修改指向 target 对象的操作。

假设需要创建一个新添加的属性值只能是数字类型的对象,就可以借助 set trap 覆盖默认的赋值行为。代码如下:

let target = {
 name: "target"
}

let proxy = new Proxy(target, {
 set(trapTarget, key, value, receiver) {
  if (!trapTarget.hasOwnProperty(key)) {
   if (isNaN(value)) {
    throw new TypeError("New property must be a number.")
   }
  }
  return Reflect.set(trapTarget, key, value, receiver)
 }
})

proxy.count = 1
console.log(proxy.count)  // 1
console.log(target.count) // 1

proxy.name = "proxy"
console.log(proxy.name)  // proxy
console.log(target.name)  // proxy

proxy.anotherName = "proxy"
// TypeError: New property must be a number.

set trap 中的四个参数含义如下:

  • trapTarget:接收新属性的对象(即 proxy 指向的 target)
  • key:新属性对应的 key
  • value:新属性对应的 value
  • receiver:通常为 proxy 自身

Reflect.set() 是与 set trap 相对应的原始方法,表示被覆盖前的默认的赋值行为。

使用 get Trap 令程序读取不存在属性时报错

JavaScript 在读取不存在的属性时并不会报错,而是返回 undefined

let target = {}
console.log(target.name) // undefined

可以借助 get trap 修改读取对象属性时的默认行为:

let proxy = new Proxy({}, {
 get(trapTarget, key, receiver) {
  if (!(key in receiver)) {
   throw new TypeError("Property " + key + " doesn't exist.")
  }
  return Reflect.get(trapTarget, key, receiver)
 }
})

proxy.name = "proxy"
console.log(proxy.name) // proxy

console.log(proxy.nme)
// TypeError: Property nme doesn't exist.

通过 deleteProperty Trap 防止删除属性

JavaScript 中使用 delete 操作符删除对象的属性:

let target = {
 name: "target",
 value: 42
}

Object.defineProperty(target, "name", { configurable: false })
console.log("value" in target)  // true

let result1 = delete target.value
console.log(result1)       // true
console.log("value" in target)  // false

let result2 = delete target.name
console.log(result2)       // false
console.log("name" in target)   // true

使用 deleteProxy Trap 防止属性被意外删除:

let target = {
 name: "target",
 value: 42
}

let proxy = new Proxy(target, {
 deleteProperty(trapTarget, key) {
  if (key === "value") {
   return false
  } else {
   return Reflect.deleteProperty(trapTarget, key)
  }
 }
})

console.log("value" in proxy)  // true

let result1 = delete proxy.value
console.log(result1)       // false
console.log("value" in proxy)  // true

let result2 = delete proxy.name
console.log(result2)       // true
console.log("name" in proxy)   // false

二、Proxy 的现实应用

logging

function makeLoggable(target) {
 return new Proxy(target, {
  get: (target, property) => {
   console.log("Reading " + property)
   return target[property]
  },

  set: (target, property, value) => {
   console.log("Writing value " + value + " to " + property)
   target[property] = value
  }
 })
}

let ninja = { name: "Yoshi" }
ninja = makeLoggable(ninja)

console.log(ninja.name)
ninja.weapon = "sword"
// Reading name
// Yoshi
// Writing value sword to weapon

性能测试

function isPrime(number) {
 if (number < 2) { return false }

 for (let i = 2; i < number; i++) {
  if (number % i === 0) { return false }
 }
 return true
}

isPrime = new Proxy(isPrime, {
 apply: (target, thisArg, args) => {
  console.time("isPrime")
  const result = target.apply(thisArg, args)
  console.timeEnd("isPrime")
  return result
 }
})

console.log(isPrime(1358765377))
// isPrime: 6815.107ms
// true

自动添加属性

function Folder() {
 return new Proxy({}, {
  get: (target, property) => {
   console.log("Reading " + property)

   if(!(property in target)) {
    target[property] = new Folder()
   }
   return target[property]
  }
 })
}

const rootFolder = new Folder()
rootFolder.ninjasDir.firstNinjaDir.ninjaFile = "yoshi.txt"
// Reading ninjasDir
// Reading firstNinjaDir
console.log(rootFolder.ninjasDir.firstNinjaDir.ninjaFile)
// Reading ninjasDir
// Reading firstNinjaDir
// Reading ninjaFile
// yoshi.txt

参考资料

https://leanpub.com/understandinges6

https://www.manning.com/books/secrets-of-the-javascript-ninja-second-edition

以上就是理解JavaScript中的Proxy 与 Reflection API的详细内容,更多关于JavaScript中的Proxy 与 Reflection API的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
js几个验证函数代码
Mar 25 Javascript
jQuery-ui中自动完成实现方法
Jun 10 Javascript
php显示当前文件所在的文件以及文件夹所有文件以树形展开
Dec 13 Javascript
JavaScript常用判断写法大全(推荐)
May 30 Javascript
微信小程序获取用户openId的实现方法
May 23 Javascript
原生js实现简单的焦点图效果实例
Dec 14 Javascript
vue项目实战总结篇
Feb 11 Javascript
vue中axios实现数据交互与跨域问题
May 12 Javascript
js blob类型url的视频下载问题的解决
Nov 29 Javascript
JavaScript运动原理基础知识详解
Apr 02 Javascript
Openlayers绘制地图标注
Sep 28 Javascript
使用jQuery实现购物车
Oct 29 jQuery
vue实践---根据不同环境,自动转换请求的url地址操作
Sep 21 #Javascript
vue实践---vue不依赖外部资源实现简单多语操作
Sep 21 #Javascript
js节流防抖应用场景,以及在vue中节流防抖的具体实现操作
Sep 21 #Javascript
微信小程序实现翻牌抽奖动画
Sep 21 #Javascript
不依任何赖第三方,单纯用vue实现Tree 树形控件的案例
Sep 21 #Javascript
微信小程序实现转盘抽奖
Sep 21 #Javascript
结合axios对项目中的api请求进行封装操作
Sep 21 #Javascript
You might like
DC动画电影《黑暗正义联盟》曝预告 5月5日上线数字平台
2020/04/09 欧美动漫
一个简单的自动发送邮件系统(二)
2006/10/09 PHP
PHP性能优化 产生高度优化代码
2011/07/22 PHP
在PHP模板引擎smarty生成随机数的方法和math函数详解
2014/04/24 PHP
教你如何快捷的使用cmd访问mysql小技巧
2014/05/26 PHP
PHP中开启gzip压缩的2种方法
2015/01/31 PHP
PHP创建XML接口示例
2019/07/04 PHP
Laravel统计一段时间间隔的数据方法
2019/10/09 PHP
thinkphp框架表单数组实现图片批量上传功能示例
2020/04/04 PHP
JQuery从头学起第三讲
2010/07/06 Javascript
JQuery筛选器全系列介绍
2013/08/27 Javascript
jQuery中replaceWith()方法用法实例
2014/12/25 Javascript
JavaScript中反正弦函数Math.asin()的使用简介
2015/06/14 Javascript
js 截取或者替换字符串中的数字实现方法
2016/06/13 Javascript
JS控制静态页面之间传递参数获取参数并应用的简单实例
2016/08/10 Javascript
基于angular中的重要指令详解($eval,$parse和$compile)
2016/10/21 Javascript
jquery事件与绑定事件
2017/03/16 Javascript
Angularjs Promise实例详解
2018/03/15 Javascript
初探Vue3.0 中的一大亮点Proxy的使用
2018/12/06 Javascript
Vue 组件注册实例详解
2019/02/23 Javascript
vue实现鼠标经过动画
2019/10/16 Javascript
webpack4 配置 ssr 环境遇到“document is not defined”
2019/10/24 Javascript
ES6 Object.assign()的用法及其使用
2020/01/18 Javascript
vue 重塑数组之修改数组指定index的值操作
2020/08/09 Javascript
浅谈django rest jwt vue 跨域问题
2018/10/26 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
2020/04/26 Python
利用Python实现某OA系统的自动定位功能
2020/05/27 Python
python操作redis数据库的三种方法
2020/09/10 Python
python 读取yaml文件的两种方法(在unittest中使用)
2020/12/01 Python
美国殿堂级滑板、冲浪、滑雪服装品牌:Volcom(钻石)
2017/04/20 全球购物
Lancer Skincare官方网站:抗衰老皮肤护理
2020/11/20 全球购物
如何手工释放资源
2013/12/15 面试题
接受捐赠答谢词
2014/01/27 职场文书
数学教学随笔感言
2014/02/17 职场文书
求职推荐信范文
2015/03/27 职场文书
社区法制宣传月活动总结
2015/05/07 职场文书