Reflect
基本要点
反射:reflect是一个内置的全局对象,它的作用就是提供了一些对象实例的拦截方法,它的用法和Math对象相似,都只有静态方法和属性,同时reflect也没有构造器,无法通过new运算符构建实例对象(无实例的全局对象);也就是说reflect相当于一个工具类,只对外提供方法进行使用。
tips 拦截方法:拦截方法是指,通过自身方法去覆盖掉原有的默认方法,这样就只会生效拦截方法的操作,而不执行默认操作(例如,setter的作用,覆盖了原有的赋值操作),
这里使用了reflect的方法就不需要再使用默认的方法了
运用示例
以下使用几个简单的reflect的对比示例
Reflect.ownKeys()
获取对象的所有属性(不限于字符串),返回一个数组 ,作用类似Object.keys
Reflect.ownKeys(target)target:获取自身属性键的目标对象。
let obj ={content: 'hello world',length: 11,char: 'utf-8'
}let Keys = Object.keys(obj) // 返回一个由属性名组成的数组
console.log(Keys); // ['content', 'length', 'char']let keys = Reflect.ownKeys(obj) // 返回一个由属性名组成的数组
console.log(keys); // ['content', 'length', 'char']
这两个方法的效果一致都能拿到对象所有的属性名,但是Object.keys只能得到对象属性的字符串值,如果一个对象的属性包含symbol类型,就无法将其正确的识别出来,而Reflect.ownKeys就可以做到。
关于symbol类型可以参考:js基本类型---symbol标识符_js symbol属于什么类型-CSDN博客
Reflect.has()
判定对象中是否存在某个属性,与 in 操作符 相同,返回Boolean值。
let isChar = Reflect.has(obj, 'char') // 判断属性是否存在
console.log(isChar); // true
Reflect.get()
返回对象(数组)中指定属性的值
Reflect.get(target, propertyKey[, receiver])// target 需要取值的目标对象// propertyKey 需要获取的值的键值// receiver 如果target对象中指定了getter,receiver则为getter调用时的this值。
let content = Reflect.get(obj, 'content') // 获取属性值
console.log(content); // hello world
Reflect.set()
静态方法 Reflect.set()
在一个对象上设置一个属性的值,返回Boolean值
Reflect.set(target, propertyKey, value)
Reflect.set(target, propertyKey, value, receiver)//target 设置属性的目标对象。//propertyKey 设置的属性的名称。//value 设置的值。// receiver 如果遇到 setter,receiver则为setter调用时的this值。
// obj.content = '你好,世界' // 设置属性值let setContent = Reflect.set(obj, 'content', '你好,世界') // 设置属性值
console.log(setContent); // true,成功时返回true,否则返回false
console.log(Reflect.get(obj, 'content') ); // 你好,世界
console.log(obj.content); // 你好,世界
Reflect.deleteProperty()
静态方法 Reflect.deleteProperty()
允许用于删除属性。它很像delete obj.key,但它是一个函数,成功时返回true,否则返回false,这里的成功是根据结果来看的,如果属性不存在,则返回true,否则返回false
Reflect.deleteProperty(target, propertyKey)// target 删除属性的目标对象。// propertyKey 需要删除的属性的名称。
delete obj.content // 删除属性
console.log(obj); // {length: 11, char: 'utf-8'}let deleteContent = Reflect.deleteProperty(obj, 'content') // 删除属性
console.log(deleteContent); // true,成功时返回true,否则返回false,这里的成功是根据结果来看的,如果属性存在,则返回true,否则返回false
console.log(obj); // {length: 11, char: 'utf-8'}
console.log(obj);
为什么使用reflect
Reflect 提供的是一整套反射能力 API,它的调用方式,参数和返回值都是统一风格的,在Reflect 出现之前我们只是利用分散的 API 完成一些动态编程的事情,让动态编程有了一个统一的使用标准
和proxy的关系
proxy是代理对象,它的构造器中就包含了和Reflect相同的方法,当使用proxy代理一个引用类型时,就可以简洁的使用Reflect来实现动态编程的效果,
Reflect和proxy的切合度很高,同时也能够避免使用过多分散API带来的隐性错误(如receiver可以解决this的指向问题)和代码的冗余
总之,reflect整合了一套API用于动态化编程,它可以方便简洁的实现proxy的动态拦截代理
tips 动态编程:响应式数据的原理,当修改页面上的某一个值时,页面效果也随之改变
Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
构造器
const p = new Proxy(target, handler)// target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。// handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为,比如访问和赋值时的而外操作。
Proxy.revocable()
Proxy.revocable()
方法可以用来创建一个可撤销的代理对象。返回一个包含了代理对象本身和它的撤销方法的可撤销 Proxy
对象。
Proxy.revocable(target, handler);
// target 将用 Proxy 封装的目标对象。可以是任何类型的对象,包括原生数组,函数,甚至可以是另外一个代理对象。// handler 一个对象,其属性是一批可选的函数,这些函数定义了对应的操作被执行时代理的行为。
代理的意义
首先代理的对象和原对象的属性和值是绑定一致的,可以理解为指向同一个内存地址,也就是说对代理对象的所有操作结果,最终都会回到原对象中,这个过程就可以实现一些其他操作,例如,改变一个对象的值时,将页面重载一遍,引起页面的变化,这个过程就是响应
一个proxy示例
let obj = {name: '张三',age: 20,sex: '男',/*** @param {any} val*/set address(val){this.address = val;return val;}
}const p = new Proxy(obj,{get: (obj,key,receiver)=>{let res = Reflect.get(obj,key,receiver);return res;},set: (obj,key,val,receiver)=>{if(typeof val == 'number'){console.log('不能设置数字')return;}let res = Reflect.set(obj,key,val,receiver);return res;}
})
p.name='李四';
p.age=18;
console.log(obj,p)
可以看到 被代理的对象obj会受到p的影响触发改变,而对代理对象属性赋值时,我们可以在中间穿插其他操作,如这里不允许对属性赋值数字,
完整代码和结果展示
reflect.js
let obj ={content: 'hello world',length: 11,char: 'utf-8'
}let Keys = Object.keys(obj) // 返回一个由属性名组成的数组
console.log(Keys); // ['content', 'length', 'char']let keys = Reflect.ownKeys(obj) // 返回一个由属性名组成的数组
console.log(keys); // ['content', 'length', 'char']let isChar = Reflect.has(obj, 'char') // 判断属性是否存在
console.log(isChar); // truelet content = Reflect.get(obj, 'content') // 获取属性值
console.log(content); // hello world
console.log(obj.content); // hello world// obj.content = '你好,世界' // 设置属性值let setContent = Reflect.set(obj, 'content', '你好,世界') // 设置属性值
console.log(setContent); // true,成功时返回true,否则返回false
console.log(Reflect.get(obj, 'content') ); // 你好,世界
console.log(obj.content); // 你好,世界delete obj.content // 删除属性
console.log(obj); // {length: 11, char: 'utf-8'}let deleteContent = Reflect.deleteProperty(obj, 'content') // 删除属性
console.log(deleteContent); // true,成功时返回true,否则返回false,这里的成功是根据结果来看的,如果属性存在,则返回true,否则返回false
console.log(obj); // {length: 11, char: 'utf-8'}
console.log(obj);
proxy.js
let obj = {name: '张三',age: 20,sex: '男',/*** @param {any} val*/set address(val){this.address = val;return val;}
}const p = new Proxy(obj,{get: (obj,key,receiver)=>{let res = Reflect.get(obj,key,receiver);return res;},set: (obj,key,val,receiver)=>{if(typeof val == 'number'){console.log('不能设置数字')return;}let res = Reflect.set(obj,key,val,receiver);return res;}
})
p.name='李四';
p.age=18;
console.log(obj,p)
更多内容
更多Reflect和proxy相关的方法和属性可以查看:
Reflect - JavaScript | MDN (mozilla.org)
Proxy() 构造函数 - JavaScript | MDN (mozilla.org)