JavaScript反射和代理怎么理解,有哪些知识点
Admin 2022-06-30 群英技术资讯 283 次浏览
反射这个概念在很多编程语言中都存在,像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
在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属性,如果没有该属性,则返回undefined
。
const obj = { name: 'jack', age: 12, get userInfo() { return this.name + ' age is ' + this.age; } } Reflect.get(obj, 'name') // jack Reflect.get(obj, 'age') // 12 Reflect.get(obj, 'userInfo') // jack age is 12 // 如果传递了receiver参数,在调用userInfo()函数时,this是指向receiver对象。 const receiverObj = { name: '小明', age: 22 }; Reflect.get(obj, 'userInfo', receiverObj) // 小明 age is 22
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 // 22 receiverObj.age // 10
Reflect.has
方法相当于name in obj
里面的in
运算符。
const obj = { name: 'jack', } obj in name // true Reflect.has(obj, 'name') // true
Reflect.deleteProperty
方法相当于delete obj[name]
,用于删除对象的属性。如果删除成功,或者被删除的属性不存在,返回true
;删除失败,被删除的属性依然存在,返回false。
const obj = { name: 'jack', } delete obj.name Reflect.deleteProperty(obj, 'name')
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
方法用于设置目标对象的原型(prototype
)。返回一个布尔值,表示是否设置成功。
const obj = { name: 'jack', } Reflect.setPrototypeOf(obj, Array.prototype); obj.length // 0
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
方法相当于Object.defineProperty
,用来为对象定义属性。
const obj = {}; Object.defineProperty(obj, 'property', { value: 0, writable: false }); Reflect.defineProperty(obj, 'property', { value: 0, writable: false });
获取指定属性的描述对象。
返回一个布尔值,表示当前对象是否可扩展。
用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。
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']
代理在编程中很有用,它可以在目标对象之前增加一层“拦截”实现一些通用逻辑。
Proxy
构造函数 Proxy
(target, handler) 参数:
target
:代理的目标对象,它可以是任何类型的对象,包括内置的数组,函数,代理对象。handler
:它是一个对象,它的属性提供了某些操作发生时的处理函数。const user = {name: 'hello'} const proxy = new Proxy(user, { get: function(target, property) { // 读取属性时触发 return 'hi'; } }); proxy.name // 'hi'
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)
用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 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 // jack proxy.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 // hello obj.age // ReferenceError: age does not exist.
用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 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
用来拦截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()
方法拦截了Object.defineProperty()
操作。
用于拦截
delete
操作,如果这个方法抛出错误或者返回false
,当前属性就无法被delete
命令删除。
getOwnPropertyDescriptor()
方法拦截Object.getOwnPropertyDescriptor()
,返回一个属性描述对象或者undefined
。
主要用来拦截获取对象原型,拦截的操作如下:
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
主要用来拦截Object.setPrototypeOf()
方法。
const handlerReturnsFalse = { setPrototypeOf(target, newProto) { return false; } }; const newProto = {}, target = {}; const p1 = new Proxy(target, handlerReturnsFalse); Object.setPrototypeOf(p1, newProto); // throws a TypeError Reflect.setPrototypeOf(p1, newProto); // returns false
方法拦截Object.isExtensible()操作。
const p = new Proxy({}, { isExtensible: function(target) { console.log('called'); return true;//也可以return 1;等表示为true的值 } }); console.log(Object.isExtensible(p)); // "called" // true
用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。
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"
用来拦截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方法拦截以下操作。
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
用于拦截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
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了一个处理react异常的ErrorBoundary组件,简单实用,代码详细,对这个组件感兴趣的朋友可以参考下
今天给大家分享的是怎样用JS实现单击表格编辑修改的功能,小编觉得挺实用的,因此分享给大家做个参考,下面有实用供大家参考,接下来跟随小编一起看看吧。
目录前言组合函数含义封装盒子任意组合compose 变体抽象能力阶段小结前言这是【JS如何函数式编程】系列文章第三篇。点赞????关注????,持续追踪????前两篇传送门:《XDM,JS如何函数式编程?看这就够了!(一)》《XDM,JS如何函数式编程?看这就够了!(二)》在第二篇,我们谈了基础之基础,重要之重要——“偏函数”,偏函数通
本文主要了解JavaScript中用宏详解的内容,一些朋友可能对于宏详解不是很了解,下文对JavaScript中用宏详解的使用有很详细的介绍,需要的朋友可以参考。
这篇文章主要介绍了angularjs循环对象属性实现动态列的思路详解,本文给大家分享一个demo代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008