反射机制的定义
通俗来讲,反射机制指的是程序在运行时能够获取自身的信息。
换一种说法
假设有一个对象obj,我们不知道他的内部结构和Api,这个时候我们通过某种机制获取了一个对象的内部结构,这种机制就叫做反射
看个简单的例子:
for(let a in window){
console.log(a);
}
window.open('http://www.163.com')
这里的for in就是一个简单的反射,另外类似于Object.keys获取对象上的属性之类的方法都是称作反射
Reflect
Recflect是一个内置的对象,它提供拦截JavaScript操作的方法
- Reflect没有构造函数,不能使用new运算符去新建,也不能将其作为一个函数去调用
- Recflect的所有属性和方法都是静态的(类似Math对象)
为什么需要Reflect对象?
把实现反射机制的方法重新归结在一起并简化操作,保持JS语意清晰和语法简单,如下示例
let key1='id';
let key2=Symbol.for('name')
let obj={[key1]:1,[key2]:2}
let keys=Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))
let keys2= Reflect.ownKeys(obj);
原来的方法必须两次获取获取对象的key值,而使用Reflect方法后,就直接一次获取了
另外,Reflect补充了一些Object对象没有的方法,比如Reflect.apply
还有,Reflect让Object操作都变成了函数行为,比如使用Reflect.has(obj,name)替换name in obj,这样代码的维护性更好
Reflect的方法
Reflect一共提供了13个静态方法
Reflect.apply()
对一个函数进行调用操作,同时可以传入一个数组作为调用参数
Reflect.apply(target,thisArgument,argumentsList)
target:目标函数
thisArgument:是target调用时绑定的字符对象
[,newTargert]:调用构造函数的数组
Reflect.construct()
对构造函数进行new操作,相当于执行new target(...args)。
Reflect.construct(target,argumentsList[,newTarget])
Reflect.construct(Date,[2018,1,1]) //输出data类型,也就是日期,Thu Feb 01 2018 00:00:00 GMT+0800 (中国标准时间)
Reflect.defineProper()
定义对象的一个属性
Reflect.defineProperty(target,property,attributes)
target:目标对象
property:要定义或修改的属性名称
attributes:属性描述
Reflect.deleteProperty()
删除对象的一个属性
Reflect.deleteProperty(target,propert)
target:目标对象
property:要删除的属性名称
Reflect.get()
查找并返回对象的属性值
Reflect.get(target,property[,receiver])
target:目标对象
property:属性名
receiver:(可选)如果遇到get,那么属性名就要去获取receiver中的属性名的值
let c={
a:1,
b:2,
get c(){
return this.a+this.b
}
}
let d={
a:4,
b:4
}
console.log(Reflect.get(c,'a',d)); //1
console.log(Reflect.get(c,'b',d)); //2
console.log(Reflect.get(c,'c',d)); //8,如果没有参数d,那么获取到的值就是3
Reflect.set()
查找并返回对象的属性值
Reflect.set(target,property,value[,receiver])
target:目标对象
property:属性名
value:设置的值
receiver:(可选)如果遇到set,this将传给目标调用
Reflect.getOwnPropertyDescriptor()
查找并返回对象的属性描述符
Reflect.getOwnPropertyDescriptor(target,propertyKey)
target:目标对象
propertyKey:属性名称
Reflect.getPrototypeOf()
返回指定对象的原型,读取对象的__proto__属性
Reflect.getPrototypeOf(target)
target:目标对象
Reflect.setPrototypeOf()
设置指定对象的原型
Reflect.setPrototypeOf(target,prototype)
target:目标对象
prototype:设置需要的原型
Reflect.has()
判断obj是否有某个属性和in功能相同
Reflect.has(targert,prototype)
target:目标对象
prototype:属性名称
let student={
a:'a'
}
//in写法
console.log('a' in student);
//has写法
console.log(Reflect.has(student,'a'));
Reflect.isExtensible()
判断一个对象是否可扩展
Reflect.isExtensible(target)
target:目标对象
Reflect.preventExtensible()
让对象变为不可扩展
Reflect.preventExtensible(target)
target:目标对象
let e={};
console.log(Reflect.isExtensible(e)); //true
Reflect.preventExtensions(e) //设置为不可扩展
console.log(Reflect.isExtensible(e)); //false
e.name='aaa'; //设置无效
console.log(e); //空对象{}
Reflect.ownKeys()
返回一个包含所有自身属性(不包含继承属性)的数组
Reflect.ownKeys(target)
target:目标对象
let f={};
Object.defineProperty(f,"name",{
value:'123',
enumerable:true
})
console.log(Reflect.ownKeys(f)); //可枚举不可枚举的都可以查询到
for(let g in f){
console.log(g);
}
Proxy
Proxy用于修改对象的某些默认行为,获取值,设置值等
语法
let p=new Proxy(target,handle);
target:用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至是另一个代理)
handle:一个对象,其属性是当执行一个操作时定义代理的行为函数
Proxy.revoceable(target,handle)
创建的对象和new Proxy()一致
handle对象支持13个代理方法,如下
handle get
拦截对象属性值的获取
get(target,property,receiver)
target:目标对象
property:属性名
receiver:所操作的对象
let students={
_key:'aaa',
name:"xiaoming"
}
let handle={
get:function(target,property,receiver){
if(property.startsWith('_')){
throw new Error("属性是私有的")
}
else{
return Reflect.get(target,property,receiver)
}
}
}
let p=new Proxy(students,handle);
console.log(p.name);
console.log(p._key);
示例就是保证以_开头的私有属性不会被访问到
handler set
拦截对象属性值的设置
set(target,property,value,receiver)
target:目标对象
property:属性名
value:属性值
receiver:所操作的对象
let students1={
_key:'aaa',
name:"xiaoming"
}
let handle1={
set:function(target,property,value,receiver){
if(property.startsWith('_')){
throw new Error("属性是私有的")
}
else{
return Reflect.set(target,property,value,receiver)
}
}
}
let p1=new Proxy(students1,handle1);
p1.name='小兰';
p1._key="xiaolan"
handler deleteProperty
target:目标对象
property:属性名
拦截对象属性的删除
handler deleteProperty(target,property)
let handle2={
deleteProperty:function(target,property){
if(property.startsWith('_')){
throw new Error("属性是私有的")
}
else{
return Reflect.deleteProperty(target,property)
}
}
}
let p2=new Proxy(students1,handle1);
delete p2._key;
保证私有属性不被删除
hander getOwnpropertyDescirptor
拦截获取对象属性描述
hander getOwnpropertyDescirptor(target,property)
target:目标对象
property:属性名
handler defineProperty
拦截定义对象属性描述
handler defineProperty(target,property,descriptor)
target:目标对象
property:属性名
descriptor:属性描述
handler has
拦截判断对象属性是否存在,包括property in proxy和reflect.has,不包括Object.prototype.hasOwnProperty
handler has(target,property)
target:目标对象
property:属性名
handler ownKeys
拦截对象自身属性key值的读取操作,返回字符串或Symbol值数组
- Object.getOwnPropertyNames()
- Object.getOwnPropertySymbols()
- Object.keys()
- for in循环
ownKeys(target)
handler apply
拦截函数调用
apply(target,ctx,args)
handler construct
拦截构造函数调用
construc(target,args)
handler getPrototyperOf
拦截获取对象的原型
- Object.prototype.__proto__
- Object.prototype.isPrototypeOf()
- Object.getPrototypeOf()
- Reflect.getPrototypeOf()
- instanceof
getPrototypeOf(target)
let student1={};
let handle3={
getPrototypeOf(target){
return student1;
}
}
let proxy1=new Proxy(student1,handle3)
console.log(Object.getPrototypeOf(proxy1)=== student1); //true
handler setPrototyperOf
拦截设置对象的原型
- Object.prototype.__proto__
- Object.setPrototypeOf()
- Reflect.setPrototypeOf()
setPrototypeOf(target,proto)
target:目标对象
proto:具体的原型
handler isExtensible
拦截对象是否可扩展
isExtensible(target)
target:目标对象
handler preventExtensible
拦截对象的组织扩展方法
preventExtensible(target)
target:目标对象