什么是反射
反射这个概念在很多编程语言中都存在,像Java,C#。
在面向对象编程中,个别会先将类和办法定义好,而后创建对象显式调用办法,比方上面的例子:
public class User{ private String name; private Date birthday; //.... public int calculateAgeByBirthday(){ // ..... }}// 调用 User u = new User("jack", new Date());u.calculateAgeByBirthday();
下面这种调用形式咱们比拟相熟,不过当你想编写一些形象框架时(框架又须要与业务定义的类进行互操作),因为你不晓得业务类的成员和办法,这时反射动静获取成员变量或调用办法。
上面例子,咱们利用反射将json转换为Java对象。
public static class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}// 应用反射调用对象setter办法。public static <T> T fill(Class<T> userClass, Map<String, Object> json) throws Exception { Field[] fields = userClass.getDeclaredFields(); T user = userClass.newInstance(); for (Field field : fields) { // 首字母大写 String name = field.getName(); char[] arr = name.toCharArray(); arr[0] = Character.toUpperCase(arr[0]); System.out.println(new String(arr)); Method method = userClass.getDeclaredMethod("set" + new String(arr), field.getType()); Object returnValue = method.invoke(user, json.get(name)); } return user;}
JavaScript中Reflect
JavaScript在ES6提供了反射内置对象Reflect
,但JavaScript外面的反射和Java反射有所不同。先看下Reflect
提供的13个静态方法。
- Reflect.apply(target, thisArg, args)
- Reflect.construct(target, args)
- Reflect.get(target, name, receiver)
- Reflect.set(target, name, value, receiver)
- Reflect.defineProperty(target, name, desc)
- Reflect.deleteProperty(target, name)
- Reflect.has(target, name)
- Reflect.ownKeys(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
- Reflect.setPrototypeOf(target, prototype)
Reflect.get(target, name, receiver)
Reflect.get
办法查找并返回target
对象的name
属性,如果没有该属性,则返回undefined
。
const obj = { name: 'jack', age: 12, get userInfo() { return this.name + ' age is ' + this.age; }}Reflect.get(obj, 'name') // jackReflect.get(obj, 'age') // 12Reflect.get(obj, 'userInfo') // jack age is 12// 如果传递了receiver参数,在调用userInfo()函数时,this是指向receiver对象。const receiverObj = { name: '小明', age: 22};Reflect.get(obj, 'userInfo', receiverObj) // 小明 age is 22
Reflect.set(target, name, value, receiver)
const obj = { name: 'jack', age: 12, set updateAge(value) { return this.age = value; },}Reflect.set(obj, 'age', 22);obj.age // 22// 如果传递了receiver参数,在调用updateAge()函数时,this是指向receiver对象。const receiverObj = { age: 0};Reflect.set(obj, 'updateAge', 10, receiverObj) // obj.age // 22receiverObj.age // 10
Reflect.has(obj, name)
Reflect.has
办法相当于name in obj
外面的in
运算符。
const obj = { name: 'jack',}obj in name // trueReflect.has(obj, 'name') // true
Reflect.deleteProperty(obj, name)
Reflect.deleteProperty
办法相当于delete obj[name]
,用于删除对象的属性。如果删除胜利,或者被删除的属性不存在,返回true
;删除失败,被删除的属性仍然存在,返回false
。
const obj = { name: 'jack',}delete obj.name Reflect.deleteProperty(obj, 'name')
Reflect.construct(target, args)
Reflect.construct
办法等同于new target(...args)
。
function User(name){ this.name = name;}const user = new User('jack');Reflect.construct(User, ['jack']);
Reflect.getPrototypeOf(obj)
Reflect.getPrototypeOf
办法用于读取对象的__proto__
属性。
Reflect.setPrototypeOf(obj, newProto)
Reflect.setPrototypeOf
办法用于设置指标对象的原型(prototype)。返回一个布尔值,示意是否设置胜利。
const obj = { name: 'jack',}Reflect.setPrototypeOf(obj, Array.prototype);obj.length // 0
Reflect.apply(func, thisArg, args)
Reflect.apply
办法相当于Function.prototype.apply.call(func, thisArg, args)
,用于绑定this
对象后执行给定函数。
const nums = [1,2,3,4,5];const min = Math.max.apply(Math, nums);// 通过 Reflect.apply 调用const min = Reflect.apply(Math.min, Math, nums);
Reflect.defineProperty(target, propertyKey, attributes)
Reflect.defineProperty
办法相当于Object.defineProperty
,用来为对象定义属性。
const obj = {};Object.defineProperty(obj, 'property', { value: 0, writable: false});Reflect.defineProperty(obj, 'property', { value: 0, writable: false});
Reflect.getOwnPropertyDescriptor(target, propertyKey)
获取指定属性的形容对象。
Reflect.isExtensible (target)
返回一个布尔值,示意以后对象是否可扩大。
Reflect.preventExtensions(target)
用于让一个对象变为不可扩大。它返回一个布尔值,示意是否操作胜利。
Reflect.ownKeys (target)
Reflect.ownKeys
办法用于返回对象的所有属性。
const obj = { name: 'jack', age: 12, get userInfo() { return this.name + ' age is ' + this.age; }}Object.getOwnPropertyNames(obj)Reflect.ownKeys(obj) // ['name', 'age', 'userInfo']
JavaScript中Proxy
代理在编程中很有用,它能够在指标对象之前减少一层“拦挡”实现一些通用逻辑。
Proxy 构造函数 Proxy(target, handler)
参数:
- target:代理的指标对象,它能够是任何类型的对象,包含内置的数组,函数,代理对象。
- handler:它是一个对象,它的属性提供了某些操作产生时的处理函数。
const user = {name: 'hello'}const proxy = new Proxy(user, { get: function(target, property) { // 读取属性时触发 return 'hi'; }});proxy.name // 'hi'
Proxy中反对的拦挡操作
- handler.get(target, property, receiver)
- handler.set(target, property, value, receiver)
- handler.has(target, property)
- handler.defineProperty(target, property, descriptor)
- handler.deleteProperty(target, property)
- handler.getOwnPropertyDescriptor(target, prop)
- handler.getPrototypeOf(target)
- handler.setPrototypeOf(target, prototype)
- handler.isExtensible(target)
- handler.ownKeys(target)
- handler.preventExtensions(target)
- handler.apply(target, thisArg, argumentsList)
- handler.construct(target, argumentsList, newTarget)
get()
用于拦挡某个属性的读取操作,能够承受三个参数,顺次为指标对象、属性名和 proxy 实例自身,其中最初一个参数可选。
const user = { name: 'jack'}// 只有属性存在才返回值,否则抛出异样。const proxy = new Proxy(user, { get: function(target, property) { if (!(property in target)) { throw new ReferenceError(`${property} does not exist.`); } return target[property]; }});proxy.name // jackproxy.age // ReferenceError: age does not exist.
咱们能够定义一些公共代理对象,而后让子对象继承。
// 只有属性存在才返回值,否则抛出异样。const proxy = new Proxy({}, { get: function(target, property) { if (!(property in target)) { throw new ReferenceError(`${property} does not exist.`); } return target[property]; }});let obj = Object.create(proxy);obj.name = 'hello'obj.name // helloobj.age // ReferenceError: age does not exist.
set()
用来拦挡某个属性的赋值操作,能够承受四个参数,顺次为指标对象、属性名、属性值和 Proxy 实例自身,其中最初一个参数可选。
// 字符类型的属性长度校验let sizeValidator = { set: function(target, property, value, receiver) { if (typeof value == 'string' && value.length > 5) { throw new RangeError('Cannot exceed 5 character.'); } target[property] = value; return true; }};const validator = new Proxy({}, sizeValidator);let obj = Object.create(validator);obj.name = '123456' // RangeError: Cannot exceed 5 character.obj.age = 12 // 12
has()
用来拦挡HasProperty
操作,即判断对象是否具备某个属性时,这个办法会失效。如in
运算符。
它承受两个参数,别离是指标对象、需查问的属性名。
const handler = { has (target, key) { if (key[0] === '_') { return false; } return key in target; }};var target = { _prop: 'foo', prop: 'foo' };var proxy = new Proxy(target, handler);'_prop' in proxy // false
defineProperty()
defineProperty()
办法拦挡了Object.defineProperty()
操作。
deleteProperty()
用于拦挡delete
操作,如果这个办法抛出谬误或者返回false
,以后属性就无奈被delete
命令删除。
getOwnPropertyDescriptor()
getOwnPropertyDescriptor()
办法拦挡Object.getOwnPropertyDescriptor()
,返回一个属性形容对象或者undefined
。
getPrototypeOf()
次要用来拦挡获取对象原型,拦挡的操作如下:
- Object.getPrototypeOf()
- Reflect.getPrototypeOf()
__proto__
- Object.prototype.isPrototypeOf()
- instanceof
const obj = {};const proto = {};const handler = { getPrototypeOf(target) { console.log(target === obj); // true console.log(this === handler); // true return proto; }};const p = new Proxy(obj, handler);console.log(Object.getPrototypeOf(p) === proto); // true
setPrototypeOf()
次要用来拦挡Object.setPrototypeOf()
办法。
const handlerReturnsFalse = { setPrototypeOf(target, newProto) { return false; }};const newProto = {}, target = {};const p1 = new Proxy(target, handlerReturnsFalse);Object.setPrototypeOf(p1, newProto); // throws a TypeErrorReflect.setPrototypeOf(p1, newProto); // returns false
isExtensible()
办法拦挡Object.isExtensible()
操作。
const p = new Proxy({}, { isExtensible: function(target) { console.log('called'); return true;//也能够return 1;等示意为true的值 }});console.log(Object.isExtensible(p)); // "called" // true
ownKeys()
用来拦挡对象本身属性的读取操作。具体来说,拦挡以下操作。
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for...in
循环。
const p = new Proxy({}, { ownKeys: function(target) { console.log('called'); return ['a', 'b', 'c']; }});console.log(Object.getOwnPropertyNames(p)); // "called"
preventExtensions()
用来拦挡Object.preventExtensions()
。该办法必须返回一个布尔值,否则会被主动转为布尔值。
这个办法有一个限度,只有指标对象不可扩大时(即Object.isExtensible(proxy)
为false
),proxy.preventExtensions
能力返回true
,否则会报错。
const p = new Proxy({}, { preventExtensions: function(target) { console.log('called'); Object.preventExtensions(target); return true; }});console.log(Object.preventExtensions(p)); // "called" // false
apply()
apply
办法拦挡以下操作。
proxy(...args)
Function.prototype.apply()
和Function.prototype.call()
Reflect.apply()
它承受三个参数,别离是指标对象、指标对象的上下文对象(this
)和指标对象的参数数组。
const handler = { apply (target, ctx, args) { return Reflect.apply(...arguments); }};
例子
const target = function () { };const handler = { apply: function (target, thisArg, argumentsList) { console.log('called: ' + argumentsList.join(', ')); return argumentsList[0] + argumentsList[1] + argumentsList[2]; }};const p = new Proxy(target, handler);p(1,2,3) // "called: 1, 2, 3" 6
construct()
用于拦挡new
命令,上面是拦挡对象的写法:
const handler = { construct (target, args, newTarget) { return new target(...args); }};
它办法承受三个参数。
target
:指标对象。args
:构造函数的参数数组。newTarget
:发明实例对象时,new
命令作用的构造函数。
留神:办法返回的必须是一个对象,指标对象必须是函数,否则就会报错。
const p = new Proxy(function() {}, { construct: function(target, argumentsList) { return 0; }});new p() // 返回值不是对象,报错const p = new Proxy({}, { construct: function(target, argumentsList) { return {}; }});new p() //指标对象不是函数,报错
观察者模式
观察者是一种很罕用的模式,它的定义是当一个对象的状态产生扭转时,所有依赖于它的对象都失去告诉并被自动更新。
咱们应用Proxy 来实现一个例子,当察看对象状态变动时,让察看函数主动执行。
观察者函数,包裹察看指标,增加察看函数。
observable
包裹察看指标,返回一个Proxy对象。observe
增加察看函数到队列。
const queuedObservers = new Set();const observe = fn => queuedObservers.add(fn);const observable = obj => new Proxy(obj, {set});// 属性扭转时,主动执行察看函数。function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result;}
例子
const user = observable({ name: 'jack', age: 20});function userInfo() { console.log(`${user.name}, ${user.age}`)}observe(userInfo);user.name = '小明'; // 小明, 20
小结
本文要点回顾,欢送留言交换。
- JavaScript中的内置Reflect。
- JavaScript中的内置Proxy。
- Proxy实现观察者模式。