理解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插件集合推荐
Apr 19 Javascript
JS面向对象编程浅析
Aug 28 Javascript
JQuery 文本框回车跳到下一个文本框示例代码
Aug 30 Javascript
jquery按回车提交数据的代码示例
Nov 05 Javascript
js设置组合快捷键/tabindex功能的方法
Nov 21 Javascript
浅析jquery某一元素重复绑定的问题
Jan 03 Javascript
WordPress中利用AJAX异步获取评论用户头像的方法
Jan 08 Javascript
详解AngularJS如何实现跨域请求
Aug 22 Javascript
JS对大量数据进行多重过滤的方法
Nov 04 Javascript
最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等
Aug 16 Javascript
Vue前端开发规范整理(推荐)
Apr 23 Javascript
实例讲解JS中pop使用方法
Jan 27 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
谷歌音乐搜索栏的提示功能php修正代码
2011/05/09 PHP
PHP中的Streams详细介绍
2014/11/12 PHP
php实现简单文件下载的方法
2015/01/30 PHP
四个常见html网页乱码问题及解决办法
2015/09/08 PHP
php微信开发之关键词回复功能
2018/06/13 PHP
定义select的边框颜色
2008/04/28 Javascript
javascript 的Document属性和方法集合
2010/01/25 Javascript
JavaScript动态调整TextArea高度的代码
2010/12/28 Javascript
Knockout visible绑定使用方法
2013/11/15 Javascript
ajax提交表单实现网页无刷新注册示例
2014/05/08 Javascript
使用phantomjs进行网页抓取的实现代码
2014/09/29 Javascript
详谈nodejs异步编程
2014/12/04 NodeJs
jquery.form.js实现将form提交转为ajax方式提交的方法
2015/04/07 Javascript
jQuery实现div拖拽效果实例分析
2016/02/20 Javascript
javascript运算符语法全面概述
2016/07/14 Javascript
AngularJS入门教程之ng-checked 指令详解
2016/08/01 Javascript
bootstrap实现每隔5秒自动轮播效果
2016/12/20 Javascript
无限循环轮播图之运动框架(原生JS实现)
2017/10/01 Javascript
ES6 Object方法扩展的应用实例分析
2019/06/25 Javascript
基于js实现复制内容到操作系统粘贴板过程解析
2019/10/11 Javascript
vue.js中使用微信扫一扫解决invalid signature问题(完美解决)
2020/04/11 Javascript
使用Python编写一个在Linux下实现截图分享的脚本的教程
2015/04/24 Python
Python 通过打码平台实现验证码的实现
2019/05/13 Python
Django缓存系统实现过程解析
2019/08/02 Python
Centos7 下安装最新的python3.8
2019/10/28 Python
python字符串的拼接方法总结
2019/11/18 Python
python中return的返回和执行实例
2019/12/24 Python
日本酒店、民宿、温泉旅馆、当地旅行团中文预订:e路东瀛
2019/12/09 全球购物
在校生汽车维修实习自我鉴定
2013/09/19 职场文书
幼儿如何来做好自我评价
2013/11/05 职场文书
中文师范生自荐信
2014/01/30 职场文书
政治学专业毕业生求职信
2014/08/11 职场文书
升学宴演讲稿
2014/09/01 职场文书
工程部主管岗位职责
2015/02/12 职场文书
房地产公司财务总监岗位职责
2015/04/03 职场文书
Python制作春联的示例代码
2022/01/22 Python