前端JavaScript中的反射和代理
1、什么是反射
反射這個(gè)概念在很多編程語言中都存在,像Java,C# 。
在面向?qū)ο缶幊讨?,一般會先將類和方法定義好,然后創(chuàng)建對象顯式調(diào)用方法,比如下面的例子:
public class User{
private String name;
private Date birthday;
//....
public int calculateAgeByBirthday(){
// .....
}
}
// 調(diào)用
User u = new User("jack", new Date());
u.calculateAgeByBirthday();
上面這種調(diào)用方式我們比較熟悉,不過當(dāng)你想編寫一些抽象框架時(shí)(框架又需要與業(yè)務(wù)定義的類進(jìn)行互操作),由于你不知道業(yè)務(wù)類的成員和方法,這時(shí)反射動(dòng)態(tài)獲取成員變量或調(diào)用方法。
下面例子,我們利用反射將json轉(zhuǎn)換為Java對象。
public static class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// 使用反射調(diào)用對象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;
}
2、JavaScript中Reflect
JavaScript在ES6提供了反射內(nèi)置對象Reflect,但JavaScript里面的反射和Java反射有所不同。先看下Reflect提供的13個(gè)靜態(tài)方法。
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)
2.1 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') // jack
Reflect.get(obj, 'age') // 12
Reflect.get(obj, 'userInfo') // jack age is 12
// 如果傳遞了receiver參數(shù),在調(diào)用userInfo()函數(shù)時(shí),this是指向receiver對象。
const receiverObj = {
name: '小明',
age: 22
};
Reflect.get(obj, 'userInfo', receiverObj) // 小明 age is 22
2.2 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參數(shù),在調(diào)用updateAge()函數(shù)時(shí),this是指向receiver對象。
const receiverObj = {
age: 0
};
Reflect.set(obj, 'updateAge', 10, receiverObj) //
obj.age // 22
receiverObj.age // 10
2.3 Reflect.has(obj, name)
Reflect.has方法相當(dāng)于name in obj里面的in運(yùn)算符。
const obj = {
name: 'jack',
}
obj in name // true
Reflect.has(obj, 'name') // true
2.4 Reflect.deleteProperty(obj, name)
Reflect.deleteProperty方法相當(dāng)于delete obj[name] ,用于刪除對象的屬性。如果刪除成功,或者被刪除的屬性不存在,返回true;刪除失敗,被刪除的屬性依然存在,返回false。
const obj = {
name: 'jack',
}
delete obj.name
Reflect.deleteProperty(obj, 'name')
2.5 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__屬性。
2.6 Reflect.setPrototypeOf(obj, newProto)
Reflect.setPrototypeOf方法用于設(shè)置目標(biāo)對象的原型(prototype)。返回一個(gè)布爾值,表示是否設(shè)置成功。
const obj = {
name: 'jack',
}
Reflect.setPrototypeOf(obj, Array.prototype);
obj.length // 0
2.7 Reflect.apply(func, thisArg, args)
Reflect.apply方法相當(dāng)于Function.prototype.apply.call(func, thisArg, args) ,用于綁定this對象后執(zhí)行給定函數(shù)。
const nums = [1,2,3,4,5]; const min = Math.max.apply(Math, nums); // 通過 Reflect.apply 調(diào)用 const min = Reflect.apply(Math.min, Math, nums);
2.8 Reflect.defineProperty(target, propertyKey, attributes)
Reflect.defineProperty方法相當(dāng)于Object.defineProperty,用來為對象定義屬性。
const obj = {};
Object.defineProperty(obj, 'property', {
value: 0,
writable: false
});
Reflect.defineProperty(obj, 'property', {
value: 0,
writable: false
});
2.9 Reflect.getOwnPropertyDescriptor(target, propertyKey)
獲取指定屬性的描述對象。
2.10 Reflect.isExtensible (target)
返回一個(gè)布爾值,表示當(dāng)前對象是否可擴(kuò)展。
2.11 Reflect.preventExtensions(target)
用于讓一個(gè)對象變?yōu)椴豢蓴U(kuò)展。它返回一個(gè)布爾值,表示是否操作成功。
2.13 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']
3、JavaScript中Proxy
代理在編程中很有用,它可以在目標(biāo)對象之前增加一層“攔截”實(shí)現(xiàn)一些通用邏輯。
Proxy 構(gòu)造函數(shù) Proxy(target, handler) 參數(shù):
target:代理的目標(biāo)對象,它可以是任何類型的對象,包括內(nèi)置的數(shù)組,函數(shù),代理對象。handler:它是一個(gè)對象,它的屬性提供了某些操作發(fā)生時(shí)的處理函數(shù)。
const user = {name: 'hello'}
const proxy = new Proxy(user, {
get: function(target, property) { // 讀取屬性時(shí)觸發(fā)
return 'hi';
}
});
proxy.name // 'hi'
3.1 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)
3.2 get()
用于攔截某個(gè)屬性的讀取操作,可以接受三個(gè)參數(shù),依次為目標(biāo)對象、屬性名和 proxy 實(shí)例本身,其中最后一個(gè)參數(shù)可選。
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.
3.3 set()
用來攔截某個(gè)屬性的賦值操作,可以接受四個(gè)參數(shù),依次為目標(biāo)對象、屬性名、屬性值和 Proxy 實(shí)例本身,其中最后一個(gè)參數(shù)可選。
// 字符類型的屬性長度校驗(yàn)
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
3.4 has()
用來攔截HasProperty操作,即判斷對象是否具有某個(gè)屬性時(shí),這個(gè)方法會生效。如in運(yùn)算符。
它接受兩個(gè)參數(shù),分別是目標(biāo)對象、需查詢的屬性名。
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
3.5 defineProperty()
defineProperty()方法攔截了Object.defineProperty()操作。
3.6 deleteProperty()
用于攔截
delete操作,如果這個(gè)方法拋出錯(cuò)誤或者返回false,當(dāng)前屬性就無法被delete命令刪除。
3.7 getOwnPropertyDescriptor()
getOwnPropertyDescriptor()方法攔截Object.getOwnPropertyDescriptor(),返回一個(gè)屬性描述對象或者undefined。
3.8 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
3.9 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 TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false
3.10 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
3.11 ownKeys()
用來攔截對象自身屬性的讀取操作。具體來說,攔截以下操作。
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()for...in循環(huán)。
const p = new Proxy({}, {
ownKeys: function(target) {
console.log('called');
return ['a', 'b', 'c'];
}
});
console.log(Object.getOwnPropertyNames(p)); // "called"
3.12 preventExtensions()
用來攔截Object.preventExtensions() 。該方法必須返回一個(gè)布爾值,否則會被自動(dòng)轉(zhuǎn)為布爾值。
這個(gè)方法有一個(gè)限制,只有目標(biāo)對象不可擴(kuò)展時(shí)(即Object.isExtensible(proxy)為false),proxy.preventExtensions才能返回true,否則會報(bào)錯(cuò)。
const p = new Proxy({}, {
preventExtensions: function(target) {
console.log('called');
Object.preventExtensions(target);
return true;
}
});
console.log(Object.preventExtensions(p)); // "called"
// false
3.13 apply()
apply方法攔截以下操作。
proxy(...args)
Function.prototype.apply()和Function.prototype.call()
Reflect.apply()
它接受三個(gè)參數(shù),分別是目標(biāo)對象、目標(biāo)對象的上下文對象(this)和目標(biāo)對象的參數(shù)數(shù)組。
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
3.14 construct()
用于攔截new命令,下面是攔截對象的寫法:
const handler = {
construct (target, args, newTarget) {
return new target(...args);
}
};
它方法接受三個(gè)參數(shù)。
target:目標(biāo)對象。args:構(gòu)造函數(shù)的參數(shù)數(shù)組。newTarget:創(chuàng)造實(shí)例對象時(shí),new命令作用的構(gòu)造函數(shù)。
注意:方法返回的必須是一個(gè)對象,目標(biāo)對象必須是函數(shù),否則就會報(bào)錯(cuò)。
const p = new Proxy(function() {}, {
construct: function(target, argumentsList) {
return 0;
}
});
new p() // 返回值不是對象,報(bào)錯(cuò)
const p = new Proxy({}, {
construct: function(target, argumentsList) {
return {};
}
});
new p() //目標(biāo)對象不是函數(shù),報(bào)錯(cuò)
4、觀察者模式
觀察者是一種很常用的模式,它的定義是當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都得到通知并被自動(dòng)更新。
我們使用Proxy 來實(shí)現(xiàn)一個(gè)例子,當(dāng)觀察對象狀態(tài)變化時(shí),讓觀察函數(shù)自動(dòng)執(zhí)行。
觀察者函數(shù),包裹觀察目標(biāo),添加觀察函數(shù)。
observable包裹觀察目標(biāo),返回一個(gè)Proxy對象。observe添加觀察函數(shù)到隊(duì)列。
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
// 屬性改變時(shí),自動(dòng)執(zhí)行觀察函數(shù)。
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
到此這篇關(guān)于前端JavaScript中的反射和代理的文章就介紹到這了,更多相關(guān)JavaScript反射和代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript實(shí)現(xiàn)的分頁函數(shù)
Javascript實(shí)現(xiàn)的分頁函數(shù)...2006-12-12
JS中的every()對空數(shù)組總返回true原理分析
這篇文章主要為大家介紹了JS中的every()對空數(shù)組總返回true原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
詳解微信小程序開發(fā)之——wx.showToast(OBJECT)的使用
本篇文章主要介紹了微信小程序開發(fā)之——wx.showToast(OBJECT)的使用,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
用Move.js配合創(chuàng)建CSS3動(dòng)畫的入門指引
這篇文章主要介紹了用Move.js配合創(chuàng)建CSS3動(dòng)畫的入門指引,文中介紹了這個(gè)JavaScript庫中的一些基本方法的使用,需要的朋友可以參考下2015-07-07
Javascript使用integrity屬性進(jìn)行安全驗(yàn)證
這篇文章主要介紹了Javascript使用integrity屬性進(jìn)行安全驗(yàn)證,在html中,script標(biāo)簽可以通過src屬性引入一個(gè)js文件,引入的js文件可以是本地的,也可以是遠(yuǎn)程的,下面我們一起來看看文章詳細(xì)內(nèi)容2021-11-11
使用純JavaScript封裝一個(gè)消息提示條功能示例詳解
這篇文章主要為大家介紹了使用純JavaScript封裝一個(gè)消息提示條功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
微信小程序中使元素占滿整個(gè)屏幕高度實(shí)現(xiàn)方法
這篇文章主要介紹了微信小程序中使元素占滿整個(gè)屏幕高度實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2016-12-12

