不会Reflect,怎么玩转Proxy --Reflect篇

前言

了解vue3源码的应该都知道内置对象Reflect,vue3底层是利用ES6的一些语法来进行构建的,核心的就包括但不限于ProxyReflect等方法。

平常大多情况下,我们很少使用Reflect这个内置对象,因为Object的方法很多都够我们用的,但是当使用Proxy这个构造函数方法时,配合Reflect会令人很意外舒畅,原因很简单,Reflect的方法与Proxy的方法数量相同,并且方法名一模一样。在相同方法的情况下,Reflect的方法所返回的值,正是Proxy方法所需要的值。

由于Reflect都是对对象的操作,了解Reflect的时候,会对比着或搭配着Object的方法来。

正文

Reflect总共有13个方法:
get
set
has
ownKeys
apply
deleteProperty
definProperty
getOwnPropertyDescriptor
getPrototypeOf
setPrototypeOf
preventExtensions
isExtensible
construct

Reflect.get

查找对象中属性的值 ,这个方法接收三个参数,第一个为目标对象,第二个为要查找的key,第三个可以理解为this指向(默认为目标对象,可选),返回所查找到的值,如果没有,则返回undefined

js 复制代码
//这个方法可以理解为是getter方法,比较简单,也好理解,只是查找一个对象中是否有这个属性
const obj = {
  name: "iceCode",
  age: 24,
};
//这里第三个参数写不写都一样,可写可不写
const ref = Reflect.get(obj, "age", obj);
console.log(ref);//24
const reft = Reflect.get(obj, "tel", obj);
console.log(reft);//undefined

Reflect.set

修改对象中属性的值,接收四个参数,第一个为目标对象(也可以是数组),第二个要修改的key,第三个要修改的值,第四个可以理解为this指向(默认为目标对象,可选),返回一个布尔值,修改成功返回true,修改失败返回false

js 复制代码
//这个方法可以理解为是setter方法,修改对象中属性的值,没有就新增,有就修改
const obj = {
  name: "iceCode",
  age: 24,
};
const ref = Reflect.set(obj, "age", 18, obj);
const ref1 = Reflect.set(obj, "tel", 138338, obj);
console.log(ref,ref1);//true
console.log(obj.age);//18
console.log(obj.tel);//138338

//对数组修改
const arr=[]
//如果目标对象是一个数组,第二个传的就是索引
const ref = Reflect.set(arr, 2, 18);
console.log(arr[2])//18

//现在对数据进行劫持一下
const new_obj = Object.defineProperty(obj, "age", {
//是否可更改
  writable: false,
});
//这种情况下就修改失败了
const ref = Reflect.set(new_obj, "age", 18);
console.log(ref);//false
console.log(new_obj.age);//24

Reflect.has

查找对象中是否存在某一个值,与in操作符一致,接收三个参数,第一个为目标对象,第二个是查找的key,第三个可以理解为this指向(默认为目标对象,可选),返回一个布尔值,可以找到就返回true,找不到就返回false

js 复制代码
//与in操作符相同
const obj = {
  name: "iceCode",
  age: 24,
};
const ref = Reflect.has(obj, "age");
const ref1 = Reflect.has(obj, "tel");
console.log(ref, ref1);//true false

Reflect.ownKeys

返回一个数组,包含目标对象所有的key,包括不可遍历的key ,接收一个参数目标对象,返回值一个数组。它包括但不限于Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))。这个方法在可以说正常写代码的时候使用的频次是Reflect方法中较多的,因为返回值可以是不可遍历的key,所以代码量也会比正常使用Object的方法少很多

js 复制代码
const sTar = Symbol("tar");
const sTal = Symbol("tal");
const obj = {
  name: "iceCode",
  age: 24,
  [sTar]: 1,
  [sTal]: 18888888,
};
const ref = Reflect.ownKeys(obj);
//在这里可以看到Symbol也是可以被读出来的
console.log(ref);//[ 'name', 'age', Symbol(tar), Symbol(tal) ]

//上面也说了,包括但不限于Symbol
//使用劫持,对对象进行劫持
const new_obj = Object.defineProperty(obj, "name", {
//是否可枚举
  enumerable: false,
});
//劫持过后,我们再打印obj或者new_obj,发现被劫持的name不会再显示了
console.log(obj)//{ age: 24, [Symbol(tar)]: 1, [Symbol(tal)]: 18888888 }

//但是使用ownKeys方法后,还是可以得到name这个key
const ref = Reflect.ownKeys(obj);
console.log(ref);//[ 'name', 'age', Symbol(tar), Symbol(tal) ]

Reflect.apply

这里可以理解为改变this指向的一个方法 ,等同于Function.prototype.apply(),接收三个参数,第一个是目标函数,第二个是this指向的对象,第三个参数列表,应为数组,返回值是调用完带着指定参数和 this 值的给定的函数后返回的结果

js 复制代码
//可以理解为改变this指向的方法  第一个参数放一个可执行的方法,然后将指向改变为第二个参数,参数三就是方内的参数
const ref = Reflect.apply((1.2).toFixed, 12.234, [2]);
console.log(ref);//12.23

//为了更好的理解,这里自己写一个函数来表达一下
当Reflect.apply方法执行的时候,这个函数就会被调用
const t = function (value) {
//这里的value就是Reflect.apply的第三个参数,只能接收数组中的第一个参数,this指向的就是12
  console.log(value, this);//2 12
  return this;
};
const f = (v) => {
//因为箭头函数没有this,所以没办法改变他的this指向,this的结果也就是undefined
  console.log(v, this);//2 undefined
  return v;
};
const ref = Reflect.apply(t, 12, [2]);
const ref1 = Reflect.apply(t, 12, [2]);
//返回值就是可以执行函数返回的结果
console.log(ref,ref1);//12 2

Reflect.deleteProperty

用于删除对象中的属性 ,接收两个参数,第一个为目标对象,第二个为要删除key

等同于delete obj.key

js 复制代码
const obj = {
  name: "iceCode",
  age: 24,
};
const ref = Reflect.deleteProperty(obj, "age");
console.log(ref, obj);//true { name: 'iceCode' }

//当对象为空时,删除任何属性都为true
const ref = Reflect.deleteProperty({}, "age");
console.log(ref);//true

//当对象属性不可配置时,是无法删除的,返回false
const ref = Reflect.deleteProperty(Object.freeze(obj), "age");
console.log(ref, obj);//false { name: 'iceCode', age: 24 }

Reflect.definProperty

此方法与Object.definProperty的作用一样,唯一不同的就是它返回一个布尔值,而Object.definProperty返回的是一个劫持之后的对象,详细看Object.defineProperty()方法

接收三个参数,第一个目标对象,第二个修改或者说是劫持的key,第三个参数是一个对象,是一些定义或修改的描述

对象里的参数为:
configurable 是否能被修改 接收一个布尔值
enumerable 能否被找到或遍历 接收一个布尔值
value 赋值 可以是任意类型
writable 是否可以被赋值 接收一个布尔值
get()方法 描述的对象的值被访问时触发
set()方法 描述的对象的值被修改时触发

js 复制代码
const obj = {
  name: "iceCode",
  age: 24,
};
//成功定义一个对象 
const ref = Reflect.defineProperty(obj, "age", { value: 18 });
console.log(ref, obj);//true { name: 'iceCode', age: 18 }

Reflect.getOwnPropertyDescriptor

返回给定属性的描述 ,描述的内容就是definProperty方法中可定义的属性,找不到则返回undefined,与Object.getOwnPropertyDescriptor类似,唯一不同的是Object.getOwnPropertyDescriptor接受的如果不是一个对象则会强制转换成一个对象处理,Reflect.getOwnPropertyDescriptor如果接受的不是一个对象,则会报错

js 复制代码
const obj = {
  name: "iceCode",
  age: 24,
};

const ref = Reflect.getOwnPropertyDescriptor(obj, "age");
//在没有做任何成立的时候,描述的内容全为true
console.log(ref);//{ value: 24, writable: true, enumerable: true, configurable: true }

//如果不是对象,会报错接收的不是一个对象
const ref = Reflect.getOwnPropertyDescriptor("obj", 0);
//Reflect.getOwnPropertyDescriptor called on non-object

//Object的这个方法则会强制转换成对象处理
const objD = Object.getOwnPropertyDescriptor("obj", 0);
console.log(objD);//{ value: 'o', writable: false, enumerable: true, configurable: false }

Reflect.getPrototypeOf

返回对象的原型 ,与Object.getPropotypeOf是一致的,只有在个别情况下两者的返回结果会有不同,接收一个参数,目标对象

js 复制代码
//在ES6之后,Reflect.getPrototypeOf只接收对象类型,Object则可以接收任意类型
const obj = {
  name: "iceCode",
  age: 24,
};
Reflect.getPrototypeOf(obj);//Object.prototype
Reflect.getPrototypeOf('obj');//Reflect.getPrototypeOf called on non-object
//解决这个问题需要强制转换成Object类型
Reflect.getPrototypeOf(Objetc('obj'));//String.prototype
Reflect.getPrototypeOf(Objetc(12));//Number.prototype

//Object.getPrototypeOf省去了转换成对象类型的操作,任意类型都可以查出
Object.getPrototypeOf(obj);//Object.prototype
Object.getPrototypeOf('obj');//String.prototype
Object.getPrototypeOf(12);//Number.prototype

Reflect.setPrototypeOf

设置对象的原型 ,接收两个参数,第一个是目标对象(设置原型的对象),第二个为对象的原型对象(类型必须是对象或null,否则报错),返回一个布尔值,表明原型是否被设置成功。与Object.setPrototypeOf一致,只有返回值不同

js 复制代码
const obj = {
  name: "iceCode",
  age: 24,
};
const ref = Reflect.setPrototypeOf(obj, { tel: "18888888888" });
//表明设置成功,并且能被访问到
console.log(ref, obj.tel);//true 18888888888

//当我们定义不可被扩展以后
Reflect.preventExtensions(obj);
const ref = Reflect.setPrototypeOf(obj, { tel: "18888888888" });
//这里不可被设置成功,所写入的值也没有
console.log(ref, obj.tel);//false undefined


//Object.setPrototypeOf则会返回原有的对象
const ref = Object.setPrototypeOf(obj, { tel: "18888888888" });
console.log(ref, obj.tel);//{ name: 'iceCode', age: 24 } 18888888888
//在定义了不可扩展之后,Object.setPrototypeOf则会报错  
Object.preventExtensions(obj);
const ref = Object.setPrototypeOf(obj, { tel: "18888888888" });//#<Object> is not extensible

Reflect.preventExtensions

阻止对象扩展(禁止添加新的属性) ,接收一个参数,目标对象,返回一个布尔值,表明是否设置成不可被扩展成功。 Object.preventExtensions类似,唯一的差别是Object.preventExtensions接受的目标对象如果不是对象类型则会强制转换成对象类型,而Reflect.preventExtensions则会报错

js 复制代码
const obj = {
  name: "iceCode",
  age: 24,
};
//定义不可被扩展
Reflect.preventExtensions(obj);
//当添加新的属性时会报错
obj.tel = 1223;//Cannot add property tel, object is not extensible
//但是这个只针对当前访问对象禁止扩展,如果你想,也可以在原型上添加新的属性
obj.__proto__.tel = 123;
//这时可以正常访问的
console.log(obj.tel); //123

Reflect.isExtensible

判断一个对象是否可扩展(即是否能添加属性) ,接收一个参数,目标对象,返回一个布尔值。与Object.isExtensible类似,唯一的差别是Object.isExtensible接受的目标对象如果不是对象类型则会强制转换成对象类型,而Reflect.isExtensible则会报错

js 复制代码
const obj = {
  name: "iceCode",
  age: 24,
};
const ref = Reflect.isExtensible(obj);
console.log(ref);//true

//当对象被定义不可扩展的时候,就会返回false
Reflect.preventExtensions(obj);
const ref = Reflect.isExtensible(obj);
console.log(ref);//false

Reflect.construct

类似于new操作符创建一个构造函数,接收三个参数,第一个构造函数,第二个为参数列表(数组),第三个参数为this指向(可选),返回值是传入的构造函数(或第三个参数的this指向)实例化的实例对象

js 复制代码
function Foo(v, t) {
  this.v = v;
  this.t = t;
}
function Bar(v, t) {
  this.f = v;
  this.b = t;
  this.name = "Bar";
}
//正常情况下 看着还没new 操作符简便呢
const ref = Reflect.construct(Foo, [1, 2], Foo);
console.log(ref);//Foo { v: 1, t: 2 }
//等同于
const nFoo = new Foo(1, 2);
console.log(nFoo);//Foo { v: 1, t: 2 }

//当要改变this指向的时候,可以看出Reflect.construct更为简洁一些
const ref = Reflect.construct(Foo, [1, 2], Bar);
console.log(ref instanceof Foo);//false
console.log(ref instanceof Bar);//true
//等价于
const nFoo = Object.create(Bar.prototype);
Foo.apply(nFoo, [1, 2]);
console.log(nFoo, ref);//Bar { v: 1, t: 2, name: 'Foo' } Bar { v: 1, t: 2, name: 'Foo' }

结尾

一般情况下,我们很少单独使用Reflect中的一些方法,但是如果要使用构造函数Proxy的时候,就可以大胆为所欲为的使用了,因为相同的方法名,相同的参数,让我们可以不用知道Proxy的各个属性需要return什么的参数,直接return Reflect对象的相同参数就可以了,记住了Reflect的方法即使用也就记住了Proxy的方法,只要玩懂了Reflect也可以轻松玩转Proxy

相关推荐
阿伟来咯~17 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端22 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱25 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai34 分钟前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨35 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
独行soc2 小时前
#渗透测试#SRC漏洞挖掘#深入挖掘XSS漏洞02之测试流程
web安全·面试·渗透测试·xss·漏洞挖掘·1024程序员节
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试