js的Proxy和Reflect

目录

  • Proxy
    • 对象的基本操作
    • 常见捕获器
      • get
      • set
      • 函数相关拦截器
  • Reflect
    • Reflect与基本语法的区别
    • Reflect常见方法
  • Proxy和Reflect

ES6之前,如果我们对对象进行 新增或者删除属性时,仅靠原有的 Object.defineProperty是无法 监听的,基于此,在 ES6中就推出了 ProxyReflect,简单地说, ProxyReflect只做了一件事,就是将 对象原有的基本操作暴露出来或者拦截原有操作来供我们使用

Proxy

如果我们想监听一个对象的所有操作,我们就可以创建一个Proxy对象用于代理原对象,之后对原对象所有的操作都将会由代理对象执行
在实例化一个Proxy的时候需要传入两个参数,一个为需要监听的对象,一个处理对象handler

let obj = {name: '张三',age: 18
}
let objProxy = new Proxy(obj, {})

很显然我们需要再handler中写一些东西,那么应该写一些什么呢
我们需要在handler中定义捕获器

对象的基本操作

在了解什么是捕获器之前,我们需要先知道对象的基本操作
事实上,无论是对对象的读取还是赋值,都会触发对象的内部方法 [[GET]][[SET]],这些方法我们平时无法在外部访问
对象一共有11种基本操作,如果对象是函数的话还会额外再多出两种基本操作,所以在JavaScript中对象一共有13种基本操作,Proxy中有13种捕获器,每种捕获器都与一个基本操作相对应,我们可以定义不同的捕获器来拦截对象原本的基本操作

常见捕获器

这些捕获器我们都定义在handler
当用户访问代理对象的属性时,代理对象的基本操作被get捕获器拦截,在拦截器中我们定义针对于原对象的操作,基于此我们才能实现对对象的全面监听
以下是一些常见的捕获器

get

get对应的是基本操作里的[[GET]],定义如下

let obj = {name: '张三',age: 18
}
let objProxy = new Proxy(obj, {get: function (target, key, receiver) {console.log(`getting ${key}!`)return target[key]}
})
console.log(objProxy.name)

结果

get函数有三个参数,target为被监听的对象,key为访问的属性名,receiver是被调用的代理对象

set

set对应基本操作里的[[SET]],它的基本定义如下

let obj = {name: '张三',age: 18
}
let objProxy = new Proxy(obj, {get: function (target, key, receiver) {console.log(`getting ${key}!`)return target[key]},set: function (target, key, value, receiver) {console.log(`setting ${key}!`)target[key] = value}
})
console.log(objProxy.name)
objProxy.age = 20
objProxy.sex = "男"

结果
set函数有四个参数,target为原对象,key为设置的属性,value为新的值,receiver为代理对象
可以看到无论是设置已有的属性还是设置未拥有的属性set都能监听到

函数相关拦截器

函数相关的拦截器一共有两个,以下是它的简单定义

let func = function (num) {console.log(1 + num)
}
let funcProxy = new Proxy(func, {apply: function (target, ctx, args) {console.log(target, ctx, args)return target(...args)}
})
funcProxy(2)
let Func = function (num) {this.num = num
}
let FuncProxy = new Proxy(Func, {construct: function (target, args) {console.log(target, args)return new target(...args)}
})
let obj = new FuncProxy(2)
console.log(obj.num)

结果

Reflect

如果说Proxy可以拦截对象的基本操作,那么Reflect就可以将对象的基本操作暴露出来直接使用

Reflect与基本语法的区别

我们先看下面两段代码

let obj = {name: "张三",age: 18
}
Reflect.set(obj, "age", 20)
let obj = {name: "张三",age: 18
}
obj.age = 20

这两种写法是在结果上相同的,区别在于Reflect是直接调用了[[SET]]方法,而第二种只是间接调用
这么说可能并不能说明Reflect基本语法的区别,让我们来看下一个例子

let obj = {name: "张三",age: 18,get info() {return `姓名:${this.name},年龄:${this.age}`}
}
console.log(obj.info)

这段代码会执行两个步骤:

  1. 寻找thisinfo是一个函数,它由obj来调用,所以this指向了obj
  2. 调用[[GET]],传入原对象,属性名,以及this,得到返回结果

而如果是Reflect的话则会不同

let obj = {name: "张三",age: 18,get info() {return `姓名:${this.name},年龄:${this.age}`}
}
Reflect.get(obj, "info", obj)

Reflectget方法所需的参数与[[GET]]方法一致,所以只会执行一个步骤

  1. 因为this已经指定,所以直接调用[[GET]]方法

因为Reflect可以指定this,所以我们可以这么写

let obj = {name: "张三",age: 18,get info() {return `姓名:${this.name},年龄:${this.age}`}
}
Reflect.get(obj, "info", { name: "李四", age: 30 })

结果

这种写法是无法在基本语法层面里展现的

Reflect常见方法

因为对象有13种基本操作,所以Reflect也有13个方法
以下是一些常用方法

  1. Reflect.get(target, key [, receiver])
    获取target上某个值
  2. Reflect.set(target, key, value [, receiver])
    设置target上某个属性的值
  3. Reflect.has(target, key)
    判断一个target上是否有key属性

Proxy和Reflect

很多时候我们都是将ProxyReflect合在一起来使用

let obj = {name: "张三",age: 18,get info() {return `姓名:${this.name},年龄:${this.age}`}
}
let objProxy = new Proxy(obj, {get(target, key) {console.log(`getting ${key}`)return target[key]}
})
console.log(objProxy.info)

我们有这么一段代码,它的运行结果如下
结果
看起来似乎没什么问题,但仔细想想好像有哪里不对,我们在访问info时同时访问了nameage,但他们并没有在控制台输出getting,但是偏偏infogetting打印出来了,我们对obj设置了get的拦截器,那所有的get操作应该都会被拦截才对,这是为什么呢
因为this的指向错了,我们在控制台打印一下this,现在把代码稍微修改一下

let obj = {name: "张三",age: 18,get info() {console.log(this)return `姓名:${this.name},年龄:${this.age}`}
}

结果
真相出来了,我们在info内的this指向了原始对象,没有经过proxy,自然没有触发拦截器,解决这个问题很简单,使用Reflect即可

let obj = {name: "张三",age: 18,get info() {console.log(this)return `姓名:${this.name},年龄:${this.age}`}
}
let objProxy = new Proxy(obj, {get(target, key, recevier) {console.log(`getting ${key}`)return Reflect.get(target, key, recevier)}
})
console.log(objProxy.info)

结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/789852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT4-QT5-VS2019-编译无法打开文件 QtCore/qchar.h

1.系统环境变量 添加QTDIR 添加PATH 2.项目配置 2.1vc目录:包含目录库目录 2.2C/C 常规:附加包含目录QT5的头文件必须在附加包含目录里配置。 2.3链接器:附加库目录 2.4QTProject Settings

镜舟科技荣获第十三届中国智能制造高峰论坛两项大奖

2024年3月29日,由e-works数字化企业网和湖北省中小企业服务中心联合主办,中国中小企业发展促进中心指导的“第十三届中国智能制造高峰论坛暨第二十一届中国智能制造岁末盘点颁奖典礼”在北京圆满落幕。本次论坛汇聚了国内外智能制造领域的专家学者、企业…

有效的职场沟通及其重要性

欢迎来到职场沟通的世界,在这里,每一个词都至关重要,沉默则能传达出无尽的信息。一个有效沟通能力不仅仅是在线办公套件中的可选配件,而是一项绝对必需的能力。今天我们与您分享有效的职场沟通及其重要性。有效的职场沟通可以增强…

Redis高可用及持久化

文章目录 一、Redis高可用1、Redis高可用概述2、Redis高可用策略 二、Redis持久化1、Redis持久化的功能2、Redis持久化的两种方式2.1 RDB持久化2.2 AOF持久化(append only file) 3、RDB持久化3.1 触发条件3.1.1 手动触发3.1.2 自动触发3.1.2.1 配置方式3…

桶排序---

1、算法概念 桶排序:一种非比较的排序算法。桶排序采用了一些分类和分治的思想,把元素的值域分成若干段,每一段对应一个桶。在排序的时候,首先把每一个元素放到其对应的桶中,再对每一个桶中的元素分别排序&#xff0c…

EF数据持久化(三层架构,公司查,改)

效果图 Model设置具体流程在下面链接中 https://blog.csdn.net/Mr_wangzu/article/details/136805824?spm1001.2014.3001.5501 DAL using System; using System.Collections.Generic; using System.Linq; using System.Web; using WebApplication2.Models; namespace WebAppli…

结构体,联合体,枚举( 2 )

目录 2.联合体 2.1联合体类型的声明 2.2联合体的特点 2.3联合体的内存大小 3.枚举 3.1枚举类型的声明 3.2枚举类型的优点 3.3枚举类型的使用 2.联合体 联合体(Union)是另一种复合数据类型,它允许我们在同一内存位置存储不同的数据类型…

UE4_动画基础_ 瞄准偏移1D(Aim Offset Blend Space 1D)

瞄准偏移1D基本上可以完成角色的向左看向右看或者向上看向下看,像混合空间1D一样只有一个轴向可用。 操作步骤: 1、新建第三人称模板项目。 2、右键——动画——瞄准偏移1D 选取骨骼 双击打开 3、瞄准偏移混合的是姿势,我们需要创建姿势。 …

结合创新!多尺度特征融合+Transformer,参数和计算成本减半

通过多尺度特征融合,模型能够捕捉到不同层次的视觉细节,而Transformer的自注意力机制能够在这些不同层次的特征之间建立联系,有效地整合全局上下文信息,让模型能够应对多样化的视觉场景和变化。 这种结合策略充分利用了二者各自的…

库位电子标签可视化管理,让仓库更智能

一、工厂仓储的目前管理现状 目前工厂仓储管理存在诸多问题,如物料数量种类多,寻找困难且耗时;盘点人力成本高且速度慢;存储位置不清晰,常找不到物料,且多发、少发、错料现象时有发生;系统与库…

SSTI模板注入(jinja2)

前面学习了SSTI中的smarty类型,今天学习了Jinja2,两种类型都是flask框架的,但是在注入的语法上还是有不同 SSTI:服务器端模板注入,也属于一种注入类型。与sql注入类似,也是通过凭借进行命令的执行&#xff…

C++ 中的 vector 的模拟实现【代码纯享】

文章目录 C 中的 vector 模拟实现1. vector 的基本概念2. vector 的基本操作3. vector 的模拟实现4.代码纯享5. 总结 C 中的 vector 模拟实现 在 C 中,vector 是一个非常重要的容器,它提供了动态数组的功能。在本篇博客中,我们将尝试模拟实现…

函数参数缺省和内联函数【C++】

文章目录 函数参数缺省函数参数缺省的条件和要求 内联函数内联函数的工作原理内联函数的定义方法内联函数的要求解决方法:直接在.h中定义内联函数的函数体 内联函数再Debug模式下默认是不展开的 函数参数缺省 顾名思义:可以少传一个/多个参数给函数&…

防止推特Twitter账号被冻结,应该选什么代理类型IP?

在处理多个 Twitter 帐号时,选择合适的代理IP对于避免大规模帐户暂停至关重要。现在,问题出现了:哪种类型的代理是满足您需求的最佳选择?下面文章将为你具体讲解推特账号冻结原因以及重点介绍如何选择代理IP。 一、推特账号被冻结…

3个 JavaScript 字符串截取方法

在 JavaScript 中,可以使用 substr()、slice() 和 substring() 方法截取字符串. substring() substring() 方法返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引直到字符串的末尾的一个子集。语法如下: str.substring(inde…

通信术语:初学者入门指南(二)

1.SAR:Synthetic Aperture Radar合成孔径雷达,是一种雷达系统,通常用于地球或行星的遥感成像。相较于传统的实孔径雷达,SAR 通过在相对较长的时间内,对来自同一地点的多个雷达反射信号进行综合处理,实现了更…

【C+ +】第一个C+ + 项目的创建及namespace命名空间解释C++中的输入输出

目录 1.创建第一个c项目 1.1项目创建 1.2 .cpp源文件建立 1.3 第一个c程序hello world对比c语言hello world 2.命名空间 2.1 C关键字 2.2 命名空间---解决c语言中的命名冲突 2.2.1 namespace命名空间用法 2.2.2 :: 预作用限定符 2.2.3 命名空间的嵌套…

【嵌入式智能产品开发实战】(七)—— 政安晨:通过ARM-Linux掌握基本技能【环境准备:树莓派】

目录 Raspberry Pi OS 下载系统镜像 使用SSH客户端登陆 升级更新 政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品开发实战 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正…

人工智能|深度学习——基于Xception实现戴口罩人脸表情识别

一、项目背景 近年来,随着人工智能技术的不断发展,人脸表情识别已经成为了计算机视觉领域中的重要研究方向之一。然而,在当前的疫情形势下,佩戴口罩已经成为了一项必要的防疫措施,但是佩戴口罩会遮挡住人脸的部分区域&…

换到idf 5.0版本后报错 jsmn could not be found

原因: idf5.0去掉了部分组件,包括jsmn,工程中adf又用到了这个组件,所以会报错。 解决办法: 升级adf到新版本即可。