1,IE 浏览器不支持 Reflect/Proxy。
Proxy用于创建一个对象的代理实例,通过处理器方法拦截对象的基本操作并可自定义操作逻辑,间接地操作目标对象。Reflect对象拥有多个静态方法,这些方法对应着对象的操作方法,可能以后新加的特性会直接加在Reflect而不是对象(Object)。Reflect的方法对应着Proxy处理器的方法。Reflect 不是构造函数,不能使用new操作符,会报错。
2,Proxy实例的创建:
let targetObj = {
a: 'hi',
b: 'hello'
};
//handler 可以是个空对象{},reciever是Proxy实例本身。
let handler = {
get: function(target, prop, reciever){
console.log('proxy get');
return Reflect.get(target, prop);
},
set: function(target, prop, value, reciever){
console.log('proxy set');
if(prop.startsWith('_')){
console.log('私有属性不可访问');
}else{
Reflect.set(target, prop, value); //当访问的属性不可写或不可配置时,返回false。
}
return true; //严格模式下,set方法必须返回truthy(true, 1, '1')。
}
};
//创建代理对象,之后不对target对象操作,而是通过对代理对象的操作间接操作目标对象。
let proxy1 = new Proxy(target, handler);
proxy1.a;
//'proxy get'
//'hi'
proxy1.a = 'haha';
//'proxy set'
//'haha'
//代理对象的操作结果会影响到目标对象,所以
target.a === 'haha'; //true.
//Proxy有个静态方法,创建Proxy实例和取消Proxy的方法。
let {proxy, revoke} = Proxy.revocable(targetObj, handler);
proxy.a; //此时的proxy实例是个拥有handler和target以及isRevoked是false的对象。
//'haha'
revoke();//调用revoke之后,proxy实例的状态发生了改变,handler和target为null,isRevoked是false
proxy.a;//此时,会报错:TypeError: Cannot perform 'get' on a proxy that has been revoked
//proxy实例可以被继承:
let obj = Object.create(proxy1, {b: 'from obj'});
Object.getPrototypeOf(obj) === proxy1;//true
obj.a;
//'proxy get'
//'haha'
obj.b;
//'from obj'
3,Proxy处理器包含着一系列可选的捕捉器,如果没有定义捕捉器,那么保持目标对象的默认行为(直接操作target)。以下是个Proxy和Reflect的应用例子(这个例子或许还不够明确地表达Proxy和Reflect的作用):
//订单
//以下是个使用Proxy代理订单对象的例子
let orderObj = {
products: [
{
name: '钢笔',
price: 12,
number: 2
},
{
name: '橡皮擦',
price: 15,
number: 3
}
],
totalPrice: null,
finalPrice: null,
coupons: [1, 5], //优惠券
offPrice: null,
totalOffPrice: null //offPrice + coupons
};
//
function orderProxyFactory(orderObj){
//操作订单的方法。
let orderOps = {
addProduct:function(newPro){
if(!newPro){
return false;
}
var products = orderObj.products;
if(!products){
console.log('warning: 订单没有products属性。');
return false;
}
var product = products.find(item => item.name === newPro.name);
if(product){
product.number +=1;
}
else{
products.push(newPro);
}
this.recalculate();
console.log('add product');
},
deleteProduct: function(name){
if(!orderObj.products || !name){
return false;
}
var products = orderObj.products;
var product = orderObj.products.find(item => item.name === name);
if(product){
var index = orderObj.products.indexOf(product);
orderObj.products.splice(index, 1);
this.recalculate();
}
console.log('delete product');
},
changeNumber: function(name, offset){ //offset 可以是正负数
if(!name || !offset){
return false;
}
if(!orderObj.products){
return false;
}
var product = orderObj.products.find(item => item.name === name);
if(product){
product.number += offset;
this.recalculate();
}
console.log('change product');
},
recalculate: function(){
if(!orderObj.products){
orderObj.products = [];
}
var couponsTotal = (orderObj.coupons || [0]).reduce((coup, total) => coup + total);
var totalPrice = 0;
for(let prod of orderObj.products){
totalPrice += prod.price * prod.number;
}
orderObj.totalPrice = totalPrice;
orderObj.offPrice = offPricePolicy(totalPrice);
orderObj.totalOffPrice = couponsTotal + orderObj.offPrice;
orderObj.finalPrice = orderObj.totalPrice - orderObj.totalOffPrice;
}
}
let handler = {
get: function(target, prop){
//属性读取操作的捕捉器。
if(prop in orderObj){
console.log('读取的是订单数据');
}
return Reflect.get(target, prop);
},
set: function(target, prop, value){
//属性设置操作的捕捉器。
if(prop in orderObj){
console.log('不能直接修改订单数据');
}else{
console.log('修改' + prop + '失败。订单没有该属性。');
}
return true;
}
};
Object.setPrototypeOf(orderOps, orderObj);
var opsProxy = new Proxy(orderOps, handler);
opsProxy.recalculate(); //初始化订单的各种价格。
return opsProxy;
function offPricePolicy(totalPrice){
if(totalPrice < 80){
return 0;
}
else if(totalPrice < 100){
return 10;
}
else if(totalPrice < 300){
return 20;
}
return 30;
}
}
//代理可以拿到订单信息。
var orderProxy = orderProxyFactory(orderObj);
orderProxy.totalPrice;
orderProxy.addProduct({name: '书包', price: 150, number: 1});
orderProxy.changeNumber('橡皮擦', 1);
orderProxy.deleteProduct('橡皮擦');
4,Proxy处理器的其他方法:
has: function(target, prop)
//has 是in 操作符的捕捉器。Reflect的has方法跟 in操作符一样。
construct:function(target, args, newTarget)
//construct是 new 操作符的捕捉器。
apply: function(target, thisArg, args)
//apply是函数调用的捕捉器。像apply和call以及函数的普通调用。
getPrototypeOf: function(target)
//Object.getPrototypeOf方法的捕捉器。
setPrototypeOf: function(target, proto)
//Object.setPrototypeOf方法的捕捉器。设置对象原型。
defineProperty: function(target, prop, attributes)
//Object.defineProperty方法的捕捉器。
deleteProperty: function(target, prop)
//delete 操作符的捕捉器。
getOwnPropertyDescriptor: function(target, prop)
//Object.getOwnPropertyDescriptor方法
ownKeys: function(target)
//Object.getOwnPropertyNames方法 和 Object.getOwnPropertySymbols方法的捕捉器;
//但是在Object.getOwnPropertyNames(proxy)时,返回值经过filter,让结果跟各自的方法一样。
isExtensible: function(target)
//Object.isExtensible方法的捕捉器。
preventExtensions: function(target)
//Object.preventExtensions方法的捕捉器。
5,Reflect:
Reflect.apply(target, thisArgument, argumentsList)
//第三个参数是必需的。一定要传数组或类数组值。
//对一个函数进行调用操作,同时可以传入一个数组作为调用参数。
//和 Function.prototype.apply() 功能类似。
Reflect.construct(target, argumentsList[, newTarget])
//对构造函数进行 new 操作,相当于执行 new target(...args)。
//第二个参数argumentsList,一定要传数组或类数组值,若没有参数就传一个空数组。
//若第三个参数有值的话,那么构造器是newTarget,
//但实例的属性跟target的实例属性(不包括原型属性)一样:
var test = Reflect.construct(function(){this.a = 'hi';}, [], function(){this.b = 'hello';});
//test1{a:'hi'};
test.a;//'hi'
test.b;//undefined
Reflect.defineProperty(target, propertyKey, attributes)
//和 Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.deleteProperty(target, propertyKey)
//作为函数的delete操作符,相当于执行 delete target[name]。
Reflect.get(target, propertyKey[, receiver])
//获取对象身上某个属性的值,类似于 target[name]。
Reflect.getOwnPropertyDescriptor(target, propertyKey)
//类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,
//则返回对应的属性描述符, 否则返回 undefined.
Reflect.getPrototypeOf(target)
//类似于 Object.getPrototypeOf()。
Reflect.has(target, propertyKey)
//判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
Reflect.isExtensible(target)
//类似于 Object.isExtensible().
Reflect.ownKeys(target)
//返回一个包含所有自身属性(不包含继承属性)的数组。
//包括不可枚举(enumerable==false)的,Symbol类型的。
//(类似于 Object.keys(), 但Object.keys()不包括 不可枚举的和Symbol类型的).
Reflect.preventExtensions(target)
//类似于 Object.preventExtensions()。返回一个Boolean。
Reflect.set(target, propertyKey, value[, receiver])
//将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
Reflect.setPrototypeOf(target, prototype)
//设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。
6,Proxy/Reflect是用来操作对象的,虽然某些操作对其他类型数据(比如数组)可用,可能是因为浏览器对他们的这些操作的实现原理一样。Proxy的作用比较强大,比如定义验证器、修正值(判断赋值是否合理,不合理大的修正)之类的。
7,参考文档:
8,其他文章:(后面再补上)