广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >Javascript的Proxy与Reflect怎么调用
  • 419
分享到

Javascript的Proxy与Reflect怎么调用

2024-04-02 19:04:59 419人浏览 薄情痞子
摘要

这篇文章主要介绍了javascript的Proxy与Reflect怎么调用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Javascript的Proxy与Reflect怎么调用

这篇文章主要介绍了javascript的Proxy与Reflect怎么调用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Javascript的Proxy与Reflect怎么调用文章都会有所收获,下面我们一起来看看吧。

Javascript的Proxy与Reflect怎么调用

ECMAScript 在 es6 规范中加入了 Proxy 与 Reflect 两个新特性,这两个新特性增强了 JavaScript 中对象访问的可控性,使得 js 模块、类的封装能够更加严密与简单,也让操作对象时的报错变得更加可控。

Proxy

Proxy,正如其名,代理。这个接口可以给指定的对象创建一个代理对象,对代理对象的任何操作,如:访问属性、对属性赋值、函数调用,都会被拦截,然后交由我们定义的函数来处理相应的操作,
JavaScript 的特性让对象有很大的操作空间,同时 JavaScript 也提供了很多方法让我们去改造对象,可以随意添加属性、随意删除属性、随意更改对象的原型……但是此前 Object 类提供的 api 有许多缺点:

  • 如果要用 Object.defineProperty 定义某个名称集合内的全部属性,只能通过枚举的方式为全部属性设置 getter 和 setter,而且由于只能每个属性创造一个函数,集合太大会造成性能问题。

  • Object.defineProperty 定义后的属性,如果仍想拥有正常的存取功能,只能将数据存放在对象的另一个属性名上或者需要另一个对象来存放数据,对于只想监听属性的场合尤为不便。

  • Object.defineProperty 无法修改类中不可重新定义的属性,例如数组的 length 属性。

  • 对于那些尚不存在且名称不好预测的属性,Object.defineProperty 爱莫能助。

  • 无法修改或阻止某些行为,如:枚举属性名、修改对象原型。

Proxy 接口的出现很好地解决了这些问题:

  • Proxy 接口将对对象的所有操作归类到数个类别中,通过 Proxy 提供的陷阱拦截特定的操作,再在我们定义的处理函数中进行逻辑判断就可以实现复杂的功能,并且还能控制比以前更多的行为。

  • Proxy 创造的代理对象以中间人形式存在,其本身并不负责存放数据,我们只需要提供代理对象给外部使用者,让外部使用者在代理对象的控制下访问我们的原对象即可。

Proxy 接口在 JS 环境中是一个构造函数:

ƒ Proxy ( target: Object, handlers: Object ) : Proxy

这个构造函数有两个参数,第一个是我们要代理的对象,第二个是包含处理各种操作的函数的对象。
下面是调用示例:

//需要代理的目标
var target = { msg: "I wish I was a bird!" }; 
//包含处理各种操作的函数的对象
var handler = {
	//处理其中一种操作的函数,此处是访问属性的操作
	get(target, property) {
		//在控制台打印访问了哪个属性
		console.log(`你访问了 ${property} 属性`);
		//实现操作的功能
		return target[property];
	}
}
//构造代理对象
var proxy = new Proxy( target , handler);
//访问代理对象
proxy.msg
//控制台: 你访问了 msg 属性
//← I wish I was a bird!

在上面的例子中,先创建了一个对象,赋值给 target ,然后再以 target 为目标创建了一个代理对象,赋值给 proxy。在作为第二个参数提供给 Proxy 构造函数的对象里有属性名为“get”的属性,是一个函数,“get”是 Proxy 接口一个陷阱的名称,Proxy 会参照我们作为第二个参数提供的对象里的属性,找到那些属性名与陷阱名相同的属性,自动设置相应的陷阱并把属性上的函数作为陷阱的处理函数。陷阱能够拦截对代理对象的特定操作,把操作的细节转换成参数传递给我们的处理函数,让处理函数去完成这一操作,这样我们就可以通过处理函数来控制对象的各种行为。
在上面的例子里,构造代理对象时提供的 get 函数就是处理访问对象属性操作的函数,代理对象拦截访问对象属性的操作并给 get 函数传递目标对象请求访问的属性名两个参数,并将函数的返回值作为访问的结果。

Proxy 的陷阱一共有13种:

陷阱名与对应的函数参数拦截的操作操作示例
get(target, property)访问对象属性target.property
target[property]
set(target, property, value, receiver)赋值对象属性target.property = value
target[property] = value
has(target, property)判断对象属性是否存在property in target
isExtensible(target)判断对象可否添加属性Object.isExtensible(target)
preventExtensions(target)使对象无法添加新属性Object.preventExtensions(target)
defineProperty(target, property, descriptor)定义对象的属性Object.defineProperty(target, property, descriptor)
deleteProperty(target, property)删除对象的属性delete target.property
delete target[property]
Object.deleteProperty(target, property)
getOwnPropertyDescriptor(target, property)获取对象自有属性的描述符Object.getOwnPropertyDescriptor(target, property)
ownKeys(target)枚举对象全部自有属性Object.getOwnPropertyNames(target).
concat(Object.getOwnPropertySymbols(target))
getPrototypeOf(target)获取对象的原型Object.getPrototypeOf(target)
setPrototypeOf(target)设置对象的原型Object.setPrototypeOf(target)
apply(target, thisArg, argumentsList)函数调用target(...arguments)
target.apply(target, thisArg, argumentsList)
construct(target, argumentsList, newTarget)构造函数调用new target(...arguments)

在上面列出的陷阱里是有拦截函数调用一类操作的,但是只限代理的对象是函数的情况下有效,Proxy 在真正调用我们提供的接管函数前是会进行类型检查的,所以通过代理让普通的对象拥有函数一样的功能这种事就不要想啦。
某一些陷阱对处理函数的返回值有要求,如果不符合要求则会抛出 TypeError 错误。限于篇幅问题,本文不深入阐述,需要了解可自行查找资料。

除了直接 new Proxy 对象外,Proxy 构造函数上还有一个静态函数 revocable,可以构造一个能被销毁的代理对象。

Proxy.revocable( target: Object, handlers: Object ) : Object

Proxy.revocable( target, handlers ) → {
	proxy: Proxy,
	revoke: ƒ ()
}

这个静态函数接收和构造函数一样的参数,不过它的返回值和构造函数稍有不同,会返回一个包含代理对象和销毁函数的对象,销毁函数不需要任何参数,我们可以随时调用销毁函数将代理对象和目标对象的代理关系断开。断开代理后,再对代理对象执行任何操作都会抛出 TypeError 错误。

//创建代理对象
var temp1 = Proxy.revocable({a:1}, {});
//← {proxy: Proxy, revoke: ƒ}
//访问代理对象
temp1.proxy.a
//← 1
//销毁代理对象
temp1.revoke();
//再次访问代理对象
temp1.proxy.a
//未捕获的错误: TypeError: Cannot perfORM 'get' on a proxy that has been revoked

弄清楚了具体的原理后,下面举例一个应用场景。
假设某个需要对外暴露的对象上有你不希望被别人访问的属性,就可以找代理对象作替身,在外部访问代理对象的属性时,针对不想被别人访问的属性返回空值或者报错:

//目标对象
var target = {
	msg: "我是鲜嫩的美少女!",
	secret: "其实我是800岁的老太婆!" //不想被别人访问的属性
};
//创建代理对象
var proxy = new Proxy( target , {
	get(target, property) {
		//如果访问 secret 就报错
		if (property == "secret") throw new Error("不允许访问属性 secret!");
		return target[property];
	}
});
//访问 msg 属性
proxy.msg
//← 我是鲜嫩的美少女!
//访问 secret 属性
proxy.secret
//未捕获的错误: 不允许访问属性 secret!

在上面的例子中,我针对对 secret 属性的访问进行了报错,守护住了“美少女”的秘密,让我们歌颂 Proxy 的伟大!
只不过,Proxy 只是在程序逻辑上进行了接管,上帝视角的控制台依然能打印代理对象完整的内容,真是遗憾……(不不不,这挺好的!)

proxy//控制台: Proxy {msg: '我是鲜嫩的美少女!', secret: '其实我是800岁的老太婆!'}

以下是关于 Proxy 的一些细节问题:

  • Proxy 在处理属性名的时候会把除 Symbol 类型外的所有属性名都转化成字符串,所以处理函数在判断属性名时需要尤其注意。

  • 对代理对象的任何操作都会被拦截,一旦代理对象被创建就没有办法再修改它本身。

  • Proxy 的代理是非常底层的,在没有主动暴露原始目标对象的情况下,没有任何办法越过代理对象访问目标对象(在控制台搞骚操作除外)。

  • Proxy 代理的目标只能是对象,不能是 JavaScript 中的原始类型。

Reflect

学过其他语言的人看到 Reflect 这个词可能会首先联想到“反射”这个概念,但 JavaScript 由于语言特性是不需要反射的,所以这里的 Reflect 其实和反射无关,是 JavaScript 给 Proxy 配套的一系列函数。
Reflect 在 JS 环境里是一个全局对象,包含了与 Proxy 各种陷阱配套的函数。

Reflect: Object
Reflect → {
	apply: ƒ apply(),
	construct: ƒ construct(),
	defineProperty: ƒ defineProperty(),
	deleteProperty: ƒ deleteProperty(),
	get: ƒ (),
	getOwnPropertyDescriptor: ƒ getOwnPropertyDescriptor(),
	getPrototypeOf: ƒ getPrototypeOf(),
	has: ƒ has(),
	isExtensible: ƒ isExtensible(),
	ownKeys: ƒ ownKeys(),
	preventExtensions: ƒ preventExtensions(),
	set: ƒ (),
	setPrototypeOf: ƒ setPrototypeOf(),
	Symbol(Symbol.toStringTag): "Reflect"
}

可以看到,Reflect 上的所有函数都对应一个 Proxy 的陷阱。这些函数接受的参数,返回值的类型,都和 Proxy 上的别无二致,可以说 Reflect 就是 Proxy 拦截的那些操作的原本实现。

那 Reflect 存在的意义是什么呢?
上文提到过,Proxy 上某一些陷阱对处理函数的返回值有要求。如果想让代理对象能正常工作,那就不得不按照 Proxy 的要求去写处理函数。或许会有人觉得只要用 Object 提供的方法不就好了,然而不能这么想当然,因为某些陷阱要求的返回值和 Object 提供的方法拿到的返回值是不同的,而且有些陷阱还会有逻辑上的要求,和 Object 提供的方法的细节也有所出入。举个简单的例子:Proxy 的 defineProperty 陷阱要求的返回值是布尔类型,成功就是 true,失败就是 false。而 Object.defineProperty 在成功的时候会返回定义的对象,失败则会报错。如此应该能够看出为陷阱编写实现的难点,如果要求简单那自然是轻松,但是要求一旦复杂起来那真是想想都头大,大多数时候我们其实只想过滤掉一部分操作而已。Reflect 就是专门为了解决这个问题而提供的,因为 Reflect 里的函数都和 Proxy 的陷阱配套,返回值的类型也和 Proxy 要求的相同,所以如果我们要实现原本的功能,直接调用 Reflect 里对应的函数就好了。

//需要代理的对象
var target = {
	get me() {return "我是鲜嫩的美少女!"} //定义 me 属性的 getter
};
//创建代理对象
var proxy = new Proxy( target , {
	//拦截定义属性的操作
	defineProperty(target, property, descriptor) {
		//如果定义的属性是 me 就返回 false 阻止
		if (property == "me") return false;
		//使用 Reflect 提供的函数实现原本的功能
		return Reflect.defineProperty(target, property, descriptor);
	}
});
//尝试重新定义 me 属性
Object.defineProperty(proxy , "me", {value: "我是800岁的老太婆!"})
//未捕获的错误: TypeError: 'defineProperty' on proxy: trap returned falsish for property 'me'
//尝试定义 age 属性
Object.defineProperty(proxy , "age", {value: 17})
//← Proxy {age: 17}

//使用 Reflect 提供的函数来定义属性
Reflect.defineProperty(proxy , "me", {value: "我是800岁的老太婆!"})
//← false
Reflect.defineProperty(proxy , "age", {value: 17})
//← true

在上面的例子里,由于我很懒,所以我在接管定义属性功能的地方“偷工减料”用了 Reflect 提供的 defineProperty 函数。用 Object.defineProperty 在代理对象上定义 me 属性时报了错,表示失败,而定义 age 属性则成功完成了。可以看到,除了被报错的 me 属性,对其他属性的定义是可以成功完成的。我还使用 Reflect 提供的函数执行了同样的操作,可以看到 Reflect 也无法越过 Proxy 的代理,同时也显示出了 Reflect 和传统方法返回值的区别。

虽然 Reflect 的好处很多,但是它也有一个问题:JS 全局上的 Reflect 对象是可以被修改的,可以替换掉里面的方法,甚至还能把 Reflect 删掉。

//备份原本的 Reflect.get
var originGet = Reflect.get;
//修改 Reflect.get
Reflect.get = function get(target ,property) {
	console.log("哈哈,你的 get 已经是我的形状了!");
	return originGet(target ,property);
};
//调用 Reflect.get
Reflect.get({a:1}, "a")
//控制台: 哈哈,你的 get 已经是我的形状了!
//← 1
//删除 Reflect 变量
delete Reflect
//← true
//访问 Reflect 变量
Reflect
//未捕获的错误: ReferenceError: Reflect is not defined

基于上面的演示,不难想到,可以通过修改 Reflect 以欺骗的方式越过 Proxy 的代理。所以如果你对安全性有要求,建议在使用 Reflect 时,第一时间将全局上的 Reflect 深度复制到你的闭包作用域并且只使用你的备份,或者将全局上的 Reflect 冻结并定引用。

关于“Javascript的Proxy与Reflect怎么调用”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Javascript的Proxy与Reflect怎么调用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网VUE频道。

--结束END--

本文标题: Javascript的Proxy与Reflect怎么调用

本文链接: https://www.lsjlt.com/news/88846.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Javascript的Proxy与Reflect怎么调用
    这篇文章主要介绍了Javascript的Proxy与Reflect怎么调用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Javascript的Proxy与Reflect怎么调用...
    99+
    2022-10-19
  • ES6中Proxy与Reflect怎么实现重载
    这篇文章主要介绍“ES6中Proxy与Reflect怎么实现重载”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ES6中Proxy与Reflect怎么实现重载”文章能帮助大家解决问题。Proxy与Re...
    99+
    2023-06-17
  • vue3响应式Proxy与Reflect的理解及基本使用实例详解
    目录正文理解Proxy与ReflectProxyReflect实践示例正文 在第四章中,作者讲述了Vue.js中响应式系统的设计与实现,这一块其实是整个框架的基石,也是MVVM中,V...
    99+
    2022-11-13
    vue3响应式Proxy Reflect vue3响应式
  • js中Proxy一定要配合Reflect使用的原因是什么
    这篇文章主要介绍“js中Proxy一定要配合Reflect使用的原因是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“js中Proxy一定要配合Reflect使用的原因是什么”文章能帮助大家解决问...
    99+
    2023-06-29
  • JavaScript中的Proxy对象怎么使用
    本篇内容主要讲解“JavaScript中的Proxy对象怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript中的Proxy对象怎么使用”吧!什么是ProxyProxy是Ja...
    99+
    2023-07-05
  • JavaScript对象管家Proxy怎么使用
    这篇文章主要介绍“JavaScript对象管家Proxy怎么使用”,在日常操作中,相信很多人在JavaScript对象管家Proxy怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaScript对...
    99+
    2023-07-05
  • JavaScript代理对象Proxy怎么创建使用
    这篇文章主要讲解了“JavaScript代理对象Proxy怎么创建使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript代理对象Proxy怎...
    99+
    2022-10-19
  • JavaScript Object.defineProperty与proxy代理模式的使用详细分析
    目录1. Object.defineProperty2. Object.defineProperties3. proxy代理模式总结1. Object.defineProperty ...
    99+
    2022-11-13
    JavaScript Object.defineProperty JavaScript proxy
  • axios的二次封装与proxy反向代理怎么使用
    本文小编为大家详细介绍“axios的二次封装与proxy反向代理怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“axios的二次封装与proxy反向代理怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-06-29
  • Flex与JavaScript中怎么实现交互中调用
    今天就跟大家聊聊有关Flex与JavaScript中怎么实现交互中调用,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、在JavaScript中调用Flex方法在Flex中可以用Ex...
    99+
    2023-06-17
  • 怎么调用javascript的方法
    这篇文章给大家分享的是有关怎么调用javascript的方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。调用javascript的方法:1、方法调用模式,this此时指向myobject;2、函数调用模式,th...
    99+
    2023-06-14
  • JavaScript栈怎么调用
    本篇内容介绍了“JavaScript栈怎么调用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是栈?栈全...
    99+
    2022-10-19
  • javascript怎么调用css
    这篇文章主要介绍了javascript怎么调用css的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇javascript怎么调用css文章都会有所收获,下面我们一起来看看吧。一、内联样式内联样式可以直接应用于HT...
    99+
    2023-07-06
  • 怎么用css3的box-reflect设计倒影效果
    这篇文章主要为大家展示了“怎么用css3的box-reflect设计倒影效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“怎么用css3的box-reflect...
    99+
    2022-10-19
  • Javascript中怎么实现函数声明与递归调用
    Javascript中怎么实现函数声明与递归调用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1、函数声明变量式声明先创建一个匿名函数,然后把...
    99+
    2022-10-19
  • JavaScript的show方法怎么调用
    本篇内容介绍了“JavaScript的show方法怎么调用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2022-10-19
  • HTML怎么调用JavaScript的结果
    在Web开发中,JavaScript被广泛用于提供动态效果和用户交互功能。但是要让JavaScript代码生效,需要在HTML文件中调用它。本文将介绍如何在HTML中调用JavaScript,并且会给出一些示例代码。一、文档头在HTML文档...
    99+
    2023-05-14
  • javascript怎么调用后台
    今天小编给大家分享一下javascript怎么调用后台的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、使用JQuery A...
    99+
    2023-07-06
  • JavaScript中怎么调用栈
    JavaScript中怎么调用栈,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1:基本概念栈(stack):用来保存简单的数据字段。堆(hea...
    99+
    2022-10-19
  • javascript怎么调用函数
    这篇文章给大家分享的是有关javascript怎么调用函数的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。javascript的调用函数方法有:1、使用makeArray函数作为它的一个方法,使用json的方式来声...
    99+
    2023-06-14
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作