一.Reflect概述
Reflect和Proxy都是es6操作对象的新API,Reflect的目的有:
1)将Object对象上的语言内部方法,加到Reflect对象上,比如Object.defineProperty。
现阶段,这些方法同时在Object和Reflect上,未来的新方法只在Reflect对象上。
意义:Reflect对象上能拿到语言内部的方法
2) 修改某些Object方法的返回结果,使其更为合理。比如Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出错误,而Reflect.defineProperty(obj, name, desc)会返回false。
// es5写法 可以看到结果抛出错误了
obj={
name: '不可扩展噢'
}
Object.preventExtensions(obj)
try {
Object.defineProperty(obj, 'age', {
value: 18
})
} catch {
console.log('抛出错误')
}
// es6写法 不会抛出错误,而是返回结果false
try {
let result = Reflect.defineProperty(obj, 'age', {
value: 20
})
console.log(result) // false
} catch {
console.log('不会抛出异常')
}
3)Object的操作变为函数行为,某些操作是命令式的,比如name in obj ,delete obj[name],现在Reflect的函数行为则是Reflect.has(obj, name),Reflect.deleteProperty(obj, name)
//Object命令写法
console.log('name' in obj) //true
//Reflect函数写法
console.log(Reflect.has(obj, 'name')) //true
4)Reflect上的方法和Proxy一一对应,只要是Proxy对象上的方法,都能在Reflect上找到相应的方法。Proxy能方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是不管Proxy怎么修改默认行为,都能通过Reflect获取默认行为
let info = new Proxy({
name: '牛牛'
}, {
set: (target, key, value, receiver)=> {
// 默认行为
let result = Reflect.set(target, key, value, receiver)
if(result) {
// 修改添加的行为
console.log(`proxy ${key} on ${target} set to ${value}`)
}
return result
}
})
console.log(info) // Proxy {name: '牛牛'}
info.name = '改名了' // 输出了日志:proxy name on [object Object] set to 改名了
console.log(info) // Proxy {name: '改名了'}、
//该例Proxy拦截了目标对象的属性赋值行为,用Reflect.set将值赋值给对象的
属性,确保完成默认行为,在此基础上添加其他行为
下一例子:Proxy
对象种的每一个拦截操作(get
、delete
、has
),内部都调用对应的Reflect
方法,保证默认行为正常执行。添加的行为,就是每一个操作都输出一行日志。
let config = new Proxy(
{
name: '红星',
age: 16
},
{
get: (target, key, receiver)=> {
console.log('get', target, key)
return Reflect.get(target, key ,receiver)
},
deleteProperty: (target, key)=> {
console.log('delete', key)
return Reflect.deleteProperty(target, key)
},
has: (target, key)=> {
console.log('has', key)
return Reflect.has(target, key)
}
}
)
if('name' in config) { // 输出日志:has name
console.log(config.name)
// get {name: '红星', age: 16} name
// 红星
delete config['age'] // delete age
}
同时Reflect写法让一些操作可读性更高:
//原写法
Function.prototype.apply.call(Math.floor, undefined, [1.87]) //1
// Fuction.prototype.apply(undefined, [1.87])
// Function.prototype.apply指向Math.floor
//现写法
Reflect.apply(Math.floor, undefined, [1.87]) //1
二. 静态方法
Reflect一共有13个静态方法:
一部分是Object的方法添加到Reflect对象上,有些做了小改动,使其更合理:
1.Reflect.defineProperty(target, name, desc) == Object.defineProperty(target, name, desc),用于为对象定义属性。
1)无法定义属性时,前者不会报错,返回false(成功返回true)
2)第一个参数不是对象时,两者都抛出错误
Object.defineProperty(1, 'foo')
// TypeError: Object.defineProperty called on non-object
Reflect.defineProperty(1, 'foo')
// TypeError: Reflect.defineProperty called on non-object
3)可以和proxy配合使用。Proxy.defineProperty
对属性赋值设置了拦截,然后使用Reflect.defineProperty
完成了赋值
const p = new Proxy({}, {
defineProperty(target, prop, descriptor) {
console.log(descriptor);
return Reflect.defineProperty(target, prop, descriptor);
}
});
p.foo = 'bar';
// {value: "bar", writable: true, enumerable: true, configurable: true}
p.foo // "bar"
2.Reflect.getPrototypeOf(target) == Object.getPrototypeOf(target),返回指定对象的原型,区别在于传参不是对象时,Object.getPrototypeOf先将参数转为对象再运行,而Reflect.getPrototypeOf会报错
const myObj = new FancyThing();
// 旧写法
Object.getPrototypeOf(myObj) === FancyThing.prototype; //true
// 新写法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype; //true
// 区别
Object.getPrototypeOf(1) // Number 先转为{[[PrimitiveValue]]: 0}对象再执行
Reflect.getPrototypeOf(1) // 报错
3.Reflect.setPrototypeOf(target, prototype) == Object.setPrototypeOf(target, prototype),将指定对象的原型设为新对象。
1)前者返回布尔值。如目标对象无法扩展,前者返回false,后者则报错
const myObj = {a: 1};
// 旧写法
Object.setPrototypeOf(myObj, Array.prototype); //返回myObj对象
// 新写法
Reflect.setPrototypeOf(myObj, Array.prototype); //返回true
Object.setPrototypeOf(Object.freeze({}), null); // 报错
Reflect.setPrototypeOf(Object.freeze({}), null); // 返回false
2)当第一个参数不是对象时,前者会报错,而后者返回第一个参数自身
Reflect.setPrototypeOf(1, {})
// TypeError: Reflect.setPrototypeOf called on non-object
Object.setPrototypeOf(1, {}) // 1
3)当第一个参数是null或undefined时,两者都报错
Reflect.setPrototypeOf(null, {})
// TypeError: Reflect.setPrototypeOf called on non-object
Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined
4.Reflect.getOwnPropertyDescriptor(target, name) == Object.getOwnPropertyDescriptor(target, name),指定对象存在该属性时,返回该属性的描述符
var myObject = {};
Object.defineProperty(myObject, 'hidden', {
value: true,
enumerable: false,
});
// 旧写法
var oldDesc = Object.getOwnPropertyDescriptor(myObject, 'hidden');
// 新写法
var newDesc = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');
console.log(oldDesc, newDesc)
// 打印都是 {value: true, writable: false, enumerable: false, configurable: false}
1)当一个参数不是对象,前者会报错,后者则返回的undefined
Reflect.getOwnPropertyDescriptor(1, 'foo')
// TypeError: Reflect.getOwnPropertyDescriptor called on non-object
Object.getOwnPropertyDescriptor(1, 'foo') //返回undefined
5.Reflect.isExtensible(target) == Object.isExtensible(target),都是返回布尔值,表示目标对象是否可以扩展
const myObject = {};
// 旧写法
Object.isExtensible(myObject) // true
// 新写法
Reflect.isExtensible(myObject) // true
1)当第一个参数不是对象,前者报错,后者返回false
Reflect.isExtensible(1) // TypeError: Reflect.isExtensible called on non-object
Object.isExtensible(1) // false
6.Reflect.preventExtensions(target) == Object.preventExtensions(target),用于让一个对象变为不可扩展。前者返回布尔值,表示是否操作成功,后者返回的是对象
var myObject = {};
// 旧写法
Object.preventExtensions(myObject) // Object {}
// 新写法
Reflect.preventExtensions(myObject) // true
1)如果参数不是对象,Object.preventExtensions
在 ES5 环境报错,在 ES6 环境返回传入的参数,而Reflect.preventExtensions
会报错。
// ES5 环境
Object.preventExtensions(1) // 报错
// ES6 环境
Object.preventExtensions(1) // 1
// 新写法
Reflect.preventExtensions(1) // 报错
7.Reflect.apply(target, thisArg, args) == Function.prototype.apply.call(target, thisArg, args),用于绑定this对象后执行函数。 如果要绑定一个函数的this
对象,可以这样写fn.apply(obj, args)
,但是如果函数定义了自己的apply
方法,就只能写成Function.prototype.apply.call(fn, obj, args)
,采用Reflect
对象可以简化这种操作。
const ages = [11, 33, 12, 54, 18, 96];
// 旧写法
const youngest = Math.min.apply(Math, ages);
const oldest = Math.max.apply(Math, ages);
const type = Object.prototype.toString.call(youngest);
// 新写法
const youngest1 = Reflect.apply(Math.min, Math, ages);
const oldest1 = Reflect.apply(Math.max, Math, ages);
const type1 = Reflect.apply(Object.prototype.toString, youngest, []);
8.Reflect.ownKeys(target),用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames
与Object.getOwnPropertySymbols
之和。
var myObject = {
foo: 1,
bar: 2,
[Symbol.for('baz')]: 3,
[Symbol.for('bing')]: 4,
};
// 旧写法
Object.getOwnPropertyNames(myObject)
// ['foo', 'bar']
Object.getOwnPropertySymbols(myObject)
//[Symbol(baz), Symbol(bing)]
// 新写法
Reflect.ownKeys(myObject)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]
1)当参数不是对象时:
console.log(Object.getOwnPropertyNames(111)) // []
console.log(Object.getOwnPropertyNames('111')) // ['0', '1', '2', 'length']
Reflect.ownKeys(111) // TypeError: Reflect.ownKeys called on non-object
一部分是Object的操作符改为Reflect的函数行为:
9.Reflect.has(target, name)
10.Reflect.deleteProperty(target, name)
11.Reflect.construct(target, args)
12.Reflect.get(target, name, receiver)类似obj[key],返回target
对象的name
属性的值,如果没有该属性,则返回undefined
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
}
Reflect.get(myObject, 'foo') // 1
Reflect.get(myObject, 'bar') // 2
Reflect.get(myObject, 'baz') // 3
1) 第三个参数用于当取值时该name属性为getter函数时,其this指向receiver
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
};
var myReceiverObject = {
foo: 4,
bar: 4,
};
Reflect.get(myObject, 'baz', myReceiverObject) // 8
2)如果第一个参数不是对象,Reflect.get
方法会报错。
Reflect.get(1, 'foo') // 报错
Reflect.get(false, 'foo') // 报错
13.Reflect.set(target, name, value, receiver)类似obj[key]=value,设置target的name属性的值为value。Reflect.set返回的布尔类型
var myObject = {
foo: 1,
set bar(value) {
return this.foo = value;
},
}
myObject.foo // 1
Reflect.set(myObject, 'foo', 2);
myObject.foo // 2
Reflect.set(myObject, 'bar', 3)
myObject.foo // 3
1)如果name
属性设置了赋值函数,则赋值函数的this
绑定receiver
var myObject = {
foo: 4,
set bar(value) {
return this.foo = value;
},
};
var myReceiverObject = {
foo: 0,
};
Reflect.set(myObject, 'bar', 1, myReceiverObject);
myObject.foo // 4
myReceiverObject.foo // 1
2)注意,如果 Proxy
对象和 Reflect
对象联合使用,前者拦截赋值操作,后者完成赋值的默认行为,而且传入了receiver
,那么Reflect.set
会触发Proxy.defineProperty
拦截。