理解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 相关文章推荐
jQuery根据纬度经度查看地图处理程序
May 08 Javascript
JavaScript Sort 的一个错误用法示例
Mar 20 Javascript
使用AngularJS处理单选框和复选框的简单方法
Jun 19 Javascript
jquery实现鼠标滑过显示二级下拉菜单效果
Aug 24 Javascript
Highcharts入门之简介
Aug 02 Javascript
js canvas实现适用于移动端的百分比仪表盘dashboard
Jul 18 Javascript
浅谈Node.js 中间件模式
Jun 12 Javascript
浅谈vue方法内的方法使用this的问题
Sep 15 Javascript
layui表单提交到后台自动封装到实体类的方法
Sep 12 Javascript
在vue中阻止浏览器后退的实例
Nov 06 Javascript
vue下canvas裁剪图片实例讲解
Apr 16 Javascript
vue-preview动态获取图片宽高并增加旋转功能的实现
Jul 29 Javascript
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
追忆往昔!浅谈收音机的百年发展历史
2021/03/01 无线电
ubuntu 编译安装php 5.3.3+memcache的方法
2010/08/05 PHP
php中替换字符串函数strtr()和str_repalce()的用法与区别
2016/11/25 PHP
PHP排序算法之堆排序(Heap Sort)实例详解
2018/04/21 PHP
PHP基于cookie实现统计在线人数功能示例
2019/01/16 PHP
javscript对象原型的一些看法
2010/09/19 Javascript
jquery ui resizable bug解决方法
2010/10/26 Javascript
解析页面加载与js函数的执行 onload or ready
2013/12/12 Javascript
Js判断CSS文件加载完毕的具体实现
2014/01/17 Javascript
JS制作简单的三级联动
2015/03/18 Javascript
javascript图片预加载完整实例
2015/12/10 Javascript
js获取上传文件的绝对路径实现方法
2016/08/02 Javascript
详解webpack-dev-server 设置反向代理解决跨域问题
2018/04/18 Javascript
为什么要使用Vuex的介绍
2019/01/19 Javascript
JS实现继承的几种常用方式示例
2019/06/22 Javascript
js实现时间日期校验
2020/05/26 Javascript
python复制文件的方法实例详解
2015/05/22 Python
Python基于回溯法子集树模板解决野人与传教士问题示例
2017/09/11 Python
手把手教你如何安装Pycharm(详细图文教程)
2018/11/28 Python
python版本五子棋的实现代码
2018/12/11 Python
Python对HTML转义字符进行反转义的实现方法
2019/04/28 Python
解决django后台样式丢失,css资源加载失败的问题
2019/06/11 Python
python+openCV利用摄像头实现人员活动检测
2019/06/22 Python
django 邮件发送模块smtp使用详解
2019/07/22 Python
Django的用户模块与权限系统的示例代码
2019/07/24 Python
pytorch中图像的数据格式实例
2020/02/11 Python
解决pycharm中opencv-python导入cv2后无法自动补全的问题(不用作任何文件上的修改)
2020/03/05 Python
python多进程 主进程和子进程间共享和不共享全局变量实例
2020/04/25 Python
基于Python制作一副扑克牌过程详解
2020/10/19 Python
python里glob模块知识点总结
2021/01/05 Python
CSS3中的transform属性进行2D和3D变换的基本用法
2016/05/12 HTML / CSS
html5的画布canvas——画出弧线、旋转的图形实例代码+效果图
2013/06/09 HTML / CSS
海南地接欢迎词
2014/01/14 职场文书
销售行政专员岗位职责
2014/06/10 职场文书
企业承诺书格式范文
2015/04/28 职场文书
私人贷款担保书该怎么写呢?
2019/07/02 职场文书