理解JavaScript中的Proxy 与 Reflection API

 更新时间:2020年09月21日 11:55:21   作者:Starryland  
这篇文章主要介绍了JavaScript中的Proxy 与 Reflection API的相关资料,帮助大家更好的理解和学习JavaScript,感兴趣的朋友可以了解下

一、创建 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的资料请关注脚本之家其它相关文章!

相关文章

  • js焦点文字滚动效果代码分享

    js焦点文字滚动效果代码分享

    这篇文章主要介绍了js焦点文字滚动效果,功能实现非常简单,推荐给大家,有需要的小伙伴可以参考下。
    2015-08-08
  • javascript打印输出json实例

    javascript打印输出json实例

    这篇文章介绍了javascript打印输出json实例,有需要的朋友可以参考一下
    2013-11-11
  • Bootstrap 模态对话框只加载一次 remote 数据的完美解决办法

    Bootstrap 模态对话框只加载一次 remote 数据的完美解决办法

    前端框架 Bootstrap 的模态对话框,可以使用 remote 选项指定一个 URL,这样对话框在第一次弹出的时候就会自动从这个地址加载数据到 .modal-body 中,但是它只会加载一次,不过通过在事件中调用 removeData() 方法可以解决这个问题,具体操作方法,大家通过本文了解下吧
    2017-07-07
  • 如何使用Bootstrap 按钮实例详解

    如何使用Bootstrap 按钮实例详解

    本章将通过实例讲解如何使用 Bootstrap 按钮。任何带有 class .btn 的元素都会继承圆角灰色按钮的默认外观。但是 Bootstrap 提供了一些选项来定义按钮的样式。具体通过本文实例详解
    2017-03-03
  • js阻止默认浏览器行为与冒泡行为的实现代码

    js阻止默认浏览器行为与冒泡行为的实现代码

    下面小编就为大家带来一篇js阻止默认浏览器行为与冒泡行为的实现代码。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • 深入理解JavaScript系列(27):设计模式之建造者模式详解

    深入理解JavaScript系列(27):设计模式之建造者模式详解

    这篇文章主要介绍了深入理解JavaScript系列(27):设计模式之建造者模式详解,建造者模式可以将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,需要的朋友可以参考下
    2015-03-03
  • 详解Javascript数据类型的转换规则

    详解Javascript数据类型的转换规则

    本文主要介绍了Javascript的基本数据类型和数据类型的转换规则。具有很好的参考价值,需要的朋友可以看下
    2016-12-12
  • iframe高度自适应及隐藏滚动条的实例详解

    iframe高度自适应及隐藏滚动条的实例详解

    这篇文章主要介绍了iframe高度自适应及隐藏滚动条的实例详解的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-09-09
  • JavaScript传递变量: 值传递?引用传递?

    JavaScript传递变量: 值传递?引用传递?

    JavaScript传递变量 值传递 引用传递,需要的朋友可以参考下。
    2011-02-02
  • XmlUtils JS操作XML工具类

    XmlUtils JS操作XML工具类

    经常用到对xml的操作,Java里面有dom和dom4j等工具类,但是Javascript就没有,所以自己写了一个,目前算作第一个版本吧。肯定还有很多地方需要改进,如果有需要用的朋友,可以把bug和需要完善改进的地方留言或评论。
    2009-10-10

最新评论