反射这个概念在很多编程语言中都存在,像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.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方法用于读取对象的__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) 参数:
const user = {name: 'hello'}
const proxy = new Proxy(user, {
get: function(target, property) { // 读取属性时触发
return 'hi';
}
});
proxy.name // 'hi'
用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 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。
主要用来拦截获取对象原型,拦截的操作如下:
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
用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。
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方法拦截以下操作。
它接受三个参数,分别是目标对象、目标对象的上下文对象(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);
}
};
它方法接受三个参数。
注意:方法返回的必须是一个对象,目标对象必须是函数,否则就会报错。
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 来实现一个例子,当观察对象状态变化时,让观察函数自动执行。
观察者函数,包裹观察目标,添加观察函数。
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
本文要点回顾,欢迎留言交流。
原文来自:https://segmentfault.com/a/1190000040829478
当我们需要大量IP进行快节奏完成业绩的时候,很多人都会想到去IP代理服务商那里购买IP代理,所以我相信很多人对于IP代理这个词已经有一定的认识了,那么还有一个词叫做:在线代理ip网页代理
一般适用情况:1、两台都有外网IP,一台服务器请求资源通过另外一个服务器,本文重点讲第一种。2、两台服务器,其中一台服务器只有内网IP,另外一台服务器有公网和内网IP。
公司项目最近出现获取访问域名、端口、IP错误现象,通过排查发现, 之前项目一直通过Nginx自定义Headers信息来获取,但最近运维人员失误操作造成自定义Header信息丢失,造成项目拿不到对应的数据。
反向代理看上去看深奥,其实不然,只是因为汉语言文化的差异导致它看上去深奥。一般反派感觉都比较厉害和神秘。要理解反向代理,我们就不得不说一下正向代理。正向代理代理的对象是客户端;反向代理代理的对象是服务端
写在教程前:为什么要反向代理cloudflare?答:缩短路由,加快cloudflare节点到大陆用户的速度,用过cloudflare的用户应该知道,这家CDN的速度在除了大陆以外的地方访问都非常快,那么又没有什么办法使其对大陆访问良好呢?
调用 new Proxy() 可常见代替其它目标 (target) 对象的代理,它虚拟化了目标,所以二者看起来功能一致。代理可拦截JS引擎内部目标的底层对象操作,这些底层操作被拦截后会触发响应特定操作的陷阱函数。
多人协作模式下,修改代理比较麻烦,而且很容易某个开发人员会修改了vue.config.js文件后提交了。第一,很容易引起冲突。 第二,很容易出现代理错误,需要排查。而且现在微服务盛行
本地开发,代理访问,防止跨域(一般通过webpack配置代理即可),特殊情况如携带一些自定义的登录cookie则需要通过自己写node,作为一种server中间层,单线程异步可以缓解服务器压力
changeOrigin的属性值为一个布尔值,如果设置为true,那么本地会虚拟一个NODE服务端接收你的请求并代你发送该请求(中间件)。[本质上是本地开了一个服务器dev-server,所有的请求都通过这里转发出去。]
我们已经知道了什么是正向代理与反向代理,这次我们就讲一下Nginx的动静分离的案例,其实质运用的就是反向代理,专门用一台服务器代理服务器上的图片资源。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!