js语法 proxy对象拦截方法详解,proxy代理一个对象(数组,函数)的操作方法

news2024/11/16 8:22:14

如果还不能理解什么是proxy代理可以参考:js语法---理解反射Reflect对象和代理Proxy对象_js代理对象-CSDN博客

proxy代理对象(数组,函数),可以拦截的操作

  • apply---拦截方法的执行(包括直接执行,通过apply,call执行),和对应的Reflect.apply
  • construct---拦截new关键字,和对应的Reflect.construct
  • defineProperty---拦截拦截目标对象的以下操作:Object.defineProperty , proxy.property='value',和对应的Reflect.defineProperty,
  • deleteProperty---拦截delete 关键字,和对应的Reflect.deleteProperty
  • get---拦截所有读取属性的操作(包括直接读取,访问原型属性),和对应的Reflect.get
  • getOwnPropertyDescriptor---拦截方法Object.getOwnPropertyDescriptor,Reflect.getOwnPropertyDescriptor
  • getPrototypeOf---拦截对原型的访问(Object.getPrototypeOf,Reflect.getPrototypeOf,Object.prototype.__proto__,Object.prototype.isPrototypeOf,instanceof),
  • has--拦截对属性的查询,(in关键字,with检查),和对应的Reflect.has
  • ownKeys---拦截读取属性集合,(Object.getOwnPropertyNames,Object.getOwnPropertySymbols,Object.keys),以及对应的Reflect.ownKeys
  • set---拦截设置属性的操作(直接赋值,通过Object.create创建的代理对象赋值),和对应的Refl.set
  • setPrototypeOf---拦截Object.setPrototypeOf方法,和对应的Reflect.setPrototypeOf

proxy的拦截主要分为两类:

  1. 拦截对属性,原型,原型的属性操作;例如,添加/删除属性,读取/修改值,读取/修改原型,
  2. 拦截特定的方法;例如Object.setPrototypeOf,Object.getOwnPropertyDescriptor,

其中,第一类是比较常用的,第二类是对特定一些方法的拦截,一般情况下用不上,

set和get---拦截属性操作

set和get是代理拦截最常见的两个方法,它们可以在代理对象读取属性和修改属性时执行一些其他操作

get: function (target, key, proxy) 
// 代理的目标对象,代理目标的属性,代理对象

set: function(target,key,value,proxy)
// 代理的目标对象,代理目标的属性,本次拦截的修改值,代理对象

以下演示了拦截基本的读取和赋值操作

const obj = {
  type: 'object',
  toString: () => {
    return this.type;
  }
}

const p = new Proxy(obj, {
  get: function (target, key, proxy) {// 代理的目标对象,代理目标的属性,代理对象(p)
    console.log("原目标对象:",target,"\n拦截访问的属性:"+key,"\n触发本次拦截的对象",proxy);// 访问属性时,触发拦截打印
    return Reflect.get(target, key);
  },
  set: function(target,key,value,proxy){
    console.log('数据试图被修改成',value,'已拦截此操作');
    const result = Reflect.set(target,key,Reflect.get(target,key)); // set,修改key属性的值,返回布尔值
    return result
  }
});

console.log(p.type);// 直接访问属性,触发get拦截,
p.type = 'Array';
console.log(p.type);// 直接访问属性,触发get拦截,

p是obj对象的代理对象,用p来代替obj操作,当访问p的type属性时触发get拦截,本次的访问结果就变成了get拦截返回的结果,若这里没有返回值,则返回undefined;

当对p的type属性进行赋值时,触发set拦截,set内阻止了赋值,再次打印结果可以看到type的值还是object

(set,get拦截替换了原本的属性访问,修改,如果proxy内没有任何拦截方法,则不会触发代理的效果,即相当于 const p = obj )

construct---拦截new关键字

proxy可以代理拦截对象,而函数(数组)也是对象的一种,所以一样可以来拦截函数的操作,construct拦截可以在函数使用new关键字时执行一些其他操作

construct:function(fn,args,proxy)
// 代理的目标函数,目标函数被调用时的参数列表,代理对象
// 定义一个构造函数
function Fn(name){
  this.name = name;
}

const People = new Proxy(Fn,{
  construct : (fn,args,proxy)=>{// 代理的目标函数,目标函数被调用时的参数列表,代理对象
    const result = new fn(...args);
    console.log('正在使用new新建对象',result)
    return result;
  }
})

const one = new People('Tom')
console.log(one);

construct拦截将原本的new操作拦截了,并由自身代替执行,如果没有返回值则返回undefined

 apply---拦截方法的执行

当proxy代理的对象是一个函数时可以设置apply拦截,否则将会产生错误,当函数直接执行时,或使用apply,call 替换执行时 触发拦截执行一些操作,

apply:function(fn,thisArg,args)
// 代理的目标函数,函数执行时的this的指向(默认指向全局,值为undefined),代理函数被调用时的参数列表,
const add = (a,b)=>a+b;
const Add = new Proxy(add,{
  apply:(fn,thisArg,args)=>{// 代理的目标函数,函数执行时的this的指向(默认指向全局,值为undefined),代理函数被调用时的参数列表,
    console.log(fn,thisArg,args);
    return fn(...args);
  }
})

console.log(Add(1,2));
let f = '代理执行';
Add.apply(f,[2,3]);// apply方法 将Add函数内部的this指向修改成f

拦截原本的函数执行(阻止原函数执行,替换为自身执行),如果没有返回值则返回undefined 

 

ownKeys---拦截读取属性集合

当读取对象的所有属性时(通常时Object.keys方法),会触发ownKeys拦截,执行一些其他操作

ownKeys:function(target) 
// 原目标对象
const info = {
  a:1,
  b:2,
  c:3
}
const Info = new Proxy(info,{
  ownKeys:(target)=>{// 原目标对象
    const result = Reflect.ownKeys(target);
    console.log('正在获取所有属性',result)
    return result;
  }
})
console.log(Object.keys(Info));
for(let key in Info){
  
}

可以看到除了直接使用Object.keys,for in循环也会获取对象的所有属性,如果没有返回值则返回undefined 

deleteProperty---拦截delete 关键字

使用delete删除对象属性时,代理对象会触发deleteProperty拦截

deleteProperty:(target,key)=>{}
// 原目标对象,当前删除的属性
const delObj = {
  del:'要删除的属性',
  other:'其他内容'
}
const DelObj = new Proxy(delObj,{
  deleteProperty:(target,key)=>{// 原目标对象,当前删除的属性
    if(key == 'del'){
      console.log('成功删除属性'+key);
      const result = Reflect.deleteProperty(target,key);
      return result;
    }else{
      console.log('删除失败,属性'+key+'不可删除')
      return false;
    }
    
  }
})

delete DelObj.del
delete DelObj.other

 

拦截了delete的删除操作,并指定了可以删除的内容,需要返回布尔值,表示删除的执行成功和失败

has--拦截对属性的查询(in 关键字)

使用in 关键字判断属性是否存在于对象中时触发

has:(target,key)=>{}
// 目标对象,查询的属性
const has = new Proxy({name:'Tom'},{
  has:(target,key)=>{// 目标对象,查询的属性
    console.log('正在查询属性',key,"是否存在")
    return Reflect.has(target,key);
  }
})

console.log('name' in has)
console.log('age' in has)

返回查询的结果,默认返回false

getPrototypeOf---拦截对原型的访问

当代理对象触发 JS 引擎读取一个对象的原型时,触发 getPrototypeOf拦截,

以下几种方式都会访问到原型

  1. Object.getPrototypeOf()
  2. Reflect.getPrototypeOf()
  3. Object.prototype.__proto__
  4. Object.prototype.isPrototypeOf()
  5. instanceof

getPrototypeOf拦截的 返回值必须是一个对象或者 null,否则将产生错误

const arr = [0,1,2,3,4];
const Arr = new Proxy(arr,{
  getPrototypeOf:(target)=>{// 原目标对象
    console.log('访问了'+target+'的原型');
    return Reflect.getPrototypeOf(target);
  }// 返回值必须是一个对象或者 null,否则将产生错误
})


console.log(Arr instanceof Array)

当返回值为null时结果为false,返回值不为null也不为对象是产生错误

其他的特定拦截

其他拦截属性,都是对特定的方法进行拦截,当代理对象使用这些特定的方法时就会触发对应的拦截,想查看这些特定方法可以参考:Proxy() 构造函数 - JavaScript | MDN (mozilla.org)

总结

proxy的代理功能

        proxy可以代理原目标对象执行一些操作,并且这些操作都是可以拦截的;例如赋值,可以在赋值的过程执行其他操作,甚至改变或者阻止赋值;

        使用proxy代理可以实现对对象(包括数组,函数,map等引用数据类型)更加精细的控制,掌控对象操作的每一个流程,vue框架的响应式原理就依赖于proxy的代理,当虚拟dom对象执行操作改变时,拦截操作并修改(重新渲染)ui视图;

关于Reflect

        Reflect对象提供了proxy拦截属性的对应方法,它的作用是还原拦截的操作,例如,Reflect.construct就是执行原本被拦截的new 关键字操作,使用Reflect可以保证原本的操作可以正常执行,并在此基础上新增操作;

        虽然使用其他方法也能达到相同的效果,例如本文construct的例子就是再执行的原构造函数的结果,但是,使用Reflect的方法和proxy的拦截属性方法的参数完全一致(Reflect.construct(fn,args,proxy)),这种用法能够更加简洁的还原原本拦截的操作

完整代码和运行结果

// proxy代理对象(数组,函数),可以拦截的操作:
// apply---拦截方法的执行(包括直接执行,通过apply,call执行),和对应的Reflect.apply
// construct---拦截new关键字,和对应的Reflect.construct
// defineProperty---拦截目标对象的以下操作:Object.defineProperty , proxy.property='value',和对应的Reflect.defineProperty,
// deleteProperty---拦截delete 关键字,和对应的Reflect.deleteProperty
// get---拦截所有读取属性的操作(包括直接读取,访问原型属性),和对应的Reflect.get
// getOwnPropertyDescriptor---拦截方法Object.getOwnPropertyDescriptor,Reflect.getOwnPropertyDescriptor
// getPrototypeOf---拦截对原型的访问(Object.getPrototypeOf,Reflect.getPrototypeOf,Object.prototype.__proto__,Object.prototype.isPrototypeOf,instanceof),
// has--拦截对属性的查询,(in关键字,with检查),和对应的Reflect.has
// ownKeys---拦截读取属性集合,(Object.getOwnPropertyNames,Object.getOwnPropertySymbols,Object.keys),以及对应的Reflect.ownKeys
// set---拦截设置属性的操作(直接赋值,通过Object.create创建的代理对象赋值),和对应的Refl.set
// setPrototypeOf---拦截Object.setPrototypeOf方法,和对应的Reflect.setPrototypeOf

const obj = {
  type: 'object',
  toString: () => {
    return this.type;
  }
}

const p = new Proxy(obj, {
  get: function (target, key, proxy) {// 代理的目标对象,代理目标的属性,代理对象(p)
    console.log("原目标对象:",target,"\n拦截访问的属性:"+key,"\n触发本次拦截的对象",proxy);// 访问属性时,触发拦截打印
    return Reflect.get(target, key);
  },
  set: function(target,key,value,proxy){
    console.log('数据试图被修改成',value,'已拦截此操作');
    const result = Reflect.set(target,key,Reflect.get(target,key)); // set,修改key属性的值,返回布尔值
    return result
  }
});

console.log(p.type);// 直接访问属性,触发get拦截,
p.type = 'Array';
console.log(p.type);// 直接访问属性,触发get拦截,


console.log('-----------分割线----------\n')

// 定义一个构造函数
function Fn(name){
  this.name = name;
}

const People = new Proxy(Fn,{
  construct : (fn,args,proxy)=>{// 代理的目标函数,代理函数被调用时的参数列表,代理对象
    const result = new fn(...args);
    console.log('正在使用new新建对象',result)
    return result;
  }
})

const one = new People('Tom')
console.log(one);

console.log('-----------分割线----------\n')

const add = (a,b)=>a+b;
const Add = new Proxy(add,{
  apply:(fn,thisArg,args)=>{// 代理的目标函数,函数执行时的this的指向(默认指向全局,值为undefined),代理函数被调用时的参数列表,
    console.log(fn,thisArg,args);
    return fn(...args);
  }
})

console.log(Add(1,2));
let f = '代理执行';
Add.apply(f,[2,3]);// apply方法 将Add函数内部的this指向修改成f

console.log('-----------分割线----------\n')

const info = {
  a:1,
  b:2,
  c:3
}
const Info = new Proxy(info,{
  ownKeys:(target)=>{// 原目标对象
    const result = Reflect.ownKeys(target);
    console.log('正在获取所有属性',result)
    return result;
  }
})
console.log(Object.keys(Info));
for(let key in Info){
  
}

console.log('-----------分割线----------\n')

const delObj = {
  del:'要删除的属性',
  other:'其他内容'
}
const DelObj = new Proxy(delObj,{
  deleteProperty:(target,key)=>{// 原目标对象,当前删除的属性
    if(key == 'del'){
      console.log('成功删除属性'+key);
      const result = Reflect.deleteProperty(target,key);
      return result;
    }else{
      console.log('删除失败,属性'+key+'不可删除')
      return false;
    }
    
  }
})

delete DelObj.del
delete DelObj.other
console.log(DelObj)

console.log('-----------分割线----------\n')

const has = new Proxy({name:'Tom'},{
  has:(target,key)=>{// 目标对象,查询的属性
    console.log('正在查询属性',key,"是否存在")
    return Reflect.has(target,key);
  }
})

console.log('name' in has)
console.log('age' in has)

console.log('-----------分割线----------\n')

const arr = [0,1,2,3,4];
const Arr = new Proxy(arr,{
  getPrototypeOf:(target)=>{// 原目标对象
    console.log('访问了'+target+'的原型');
    return Reflect.getPrototypeOf(target);
  }// 返回值必须是一个对象或者 null,否则将产生错误
})


console.log(Arr instanceof Array)
Arr.push(5)

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

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

相关文章

【element ui】input输入控件绑定粘贴事件,从 Excel 复制的数据粘贴到输入框(el-input)时自动转换为逗号分隔的数据

目录 1、需求2、实现思路:3、控件绑定粘贴事件事件修饰符说明: 4、代码实现🚀写在最后 1、需求 在 Vue 2 和 Element UI 中,要实现从 Excel 复制空格分隔的数据,并在粘贴到输入框(el-input)时自动转换为逗号分隔的数据…

Pycharm 和虚拟环境的那些事?

背景: 我既有 python 又有Anaconda Pycharm新建虚拟环境: 只说两种方式 通过Virualenv Environment新建: 这里我们勾选上 Make available to all projects ,之后点击🆗 然后可以发现只有非常少的包,因为没有勾选继承 编译器的包 创建的虚拟环境一般目录如下&…

【Git多人协作开发】知识点总结

目录 知识点总结 1.创建dev分支开发 1.1在本地创建 1.1在远程创建(推荐) 2.远程分支和本地分支建立连接☞pull和push操作 2.1情况1 2.2情况2 2.3情况3 3.本地仓库对远程仓库的拉取pull操作 3.1情况1 3.2情况2 4.将开发分支的内容合并到远程m…

充电桩浪涌保护方案—保障充电设施安全稳定运行的关键

在当今新能源汽车蓬勃发展的时代,充电桩作为电动汽车的“加油站”,其重要性不言而喻。然而,由于其复杂的电气环境和暴露于户外的特点,充电桩容易受到浪涌的影响。浪涌可能来自雷电、电网故障、大功率设备的启停等,对充…

在 Windows 上安装 PostgreSQL

官网下载地址: https://www.enterprisedb.com/downloads/postgres-postgresql-downloadsWindows平台 官网直接提供exe安装包,没有手动安装的压缩包 postgresql-14.4-1-windows-x64.exe几个重要的安装选项 安装界面会指定服务程序和库两个路径&#xf…

5G赋能车联网,无人驾驶引领未来出行

无人驾驶车联网应用已成为智能交通领域的重要发展趋势。随着无人驾驶技术的不断进步和5G网络的广泛部署,5G工业路由器在无人驾驶车联网中的应用日益广泛,为无人驾驶车辆提供了稳定、高效、低时延的通信保障。 5G工业路由器的优势 低时延:5G网…

【STM32 HAL库】ADC

ADC,顾名思义就是模拟信号->数字信号ADC工作原理 分类: 并联比较型-----转换速度快-----成本高、功耗高、分辨率低 分压部分比较部分编码部分(其中Vx为模拟电压输入 ) 逐次逼近型-----结构简单,功耗低-----转换速…

electron 网页TodoList应用打包win桌面软件数据持久化

参考: electron 网页TodoList工具打包成win桌面应用exe https://blog.csdn.net/weixin_42357472/article/details/140648621 electron直接打包exe应用,打开网页上面添加的task在重启后为空,历史没有被保存,需要持久化工具保存之前…

汽车免拆诊断案例 | 2018 款别克阅朗车蓄电池偶尔亏电

故障现象 一辆2018款别克阅朗车,搭载LI6发动机和GF6变速器,累计行驶里程约为9.6万km。车主反映,该车停放一晚后,蓄电池偶尔亏电。 故障诊断 接车后用虹科Pico汽车示波器和高精度电流钳(30 A)测量该车的寄…

ElementUI,修改el-table中的数据,视图无法及时更新

需求&#xff1a;点击table表格中的“修改”之后&#xff0c;当前行变为可输入状态的行&#xff0c;点击“确定”后变为普通表格&#xff1b; 先贴上已经完美解决问题的代码 实现代码&#xff1a; <section><div style"display: flex;justify-content: space-b…

数据中心服务器搬迁团队

数据中心机房服务器搬迁&#xff0c;需要专业的数据中心机房服务器提供技术保障服务。友力科技&#xff08;广州&#xff09;有限公司&#xff0c;作为华南地区主流的数据中心服务商&#xff0c;专业从事数据中心机房搬迁服务。 数据中心机房搬迁涉及设备数量多、系统复杂&…

基于联盟链Fabric 2.X 的中药饮片代煎配送服务与监管平台

业务背景 近年来&#xff0c;随着公众对中医药青睐有加&#xff0c;中药代煎服务作为中医药现代化的重要一环&#xff0c;在全国各地蓬勃兴起。鉴于传统煎煮方式的繁琐耗时&#xff0c;医疗机构纷纷转向与第三方中药饮片企业合作&#xff0c;采用集中代煎模式。这些第三方煎药中…

goenv丝滑控制多版本go

安装 先装下goenv brew install goenv去 ~/.bash_profile 添加一下 export GOENV_ROOT"$HOME/.goenv" export PATH"$GOENV_ROOT/bin:$PATH" eval "$(goenv init -)"执行一下让配置生效 source ~/.bash_profile插一嘴&#xff0c;如果之前是在…

【BUG】已解决:No Python at ‘C:Users…Python Python39python. exe’

No Python at ‘C:Users…Python Python39python. exe’ 目录 No Python at ‘C:Users…Python Python39python. exe’ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班…

独立游戏《星尘异变》UE5 C++程序开发日志8——实现敏感词过滤功能(AC自动机)

在游戏中经常会有需要玩家输入一些内容的功能&#xff0c;例如聊天&#xff0c;命名等&#xff0c;这款游戏只有在存档时辉用到命名功能&#xff0c;所以这个过滤也只是一个实验性的功能&#xff0c;我们将使用AC自动机来实现&#xff0c;这是在我们把“csdn”这个词设置为屏蔽…

nginx代理缓存配置-Linux(CentOS)

代理缓存 1. 编写主配置文件2. 编辑虚拟机配置文件3. 重启nginx服务 nginx代理服务配置&#xff0c;基于http协议 开启代理缓存的前提是已经开启了代理服务&#xff0c;请确保已经开启代理服务 1. 编写主配置文件 主配置文件通常在/etc/nginx/nginx.conf&#xff0c;在该文件…

【Vue3】watch 监视 ref 定义的数据

【Vue3】watch 监视 ref 定义的数据 背景简介开发环境开发步骤及源码参数说明 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努…

江科大/江协科技 STM32学习笔记P6

文章目录 LED闪烁&LE流水&蜂鸣器一、操作STM32的GPIO步骤二、RCC库函数什么是AHB与APB&#xff1f; 三、GPIO库函数GPIO初始化选择IO接口工作方式 四、四种方法实现LED闪灯 LED闪烁&LE流水&蜂鸣器 一、操作STM32的GPIO步骤 1、使用RCC开启GPIO的时钟 2、使用…

【C语言报错已解决】Use of Uninitialized Variable

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言&#xff1a; 在编程中&#xff0c;未初始化的变量是一个常见的问题&#xff0c;它可能导致程序的行为变得不可预测。未初…

CAD图块的对齐方法的使用技巧

对齐功能配合鼠标技巧才能正常使用&#xff0c;不然对齐的图形胡乱翻转。