Proxy
和Reflect
是 ES6 新增 API。javascript
Reflect
是一个内置的对象,它提供拦截 JavaScript 操做的方法。Reflect不是一个函数对象,所以它是不可构造的。Reflect
的全部的方法都是静态的就和Math
同样,目前它尚未静态属性。java
Reflect
对象的方法与Proxy
对象的方法相同。编程
Reflect
一共有13个静态方法:数组
它能够分为一部分是是原来存在Object
上的方法,将它转义到了Reflect
上,并做了小改动,让方法更加合理。app
defineProperty
与Object.defineProperty相似,可是当对象没法定义时Object.defineProperty
会报错而Reflect.defineProperty
不会,它会返回false
,成功时返回true
,若是不是对象仍是会报错。getPrototypeOf(target)
与Object.getPrototypeOf
同样,返回指定对象的原型。setPrototypeOf(target, prototype)
与Object.setPrototypeOf
同样,它将指定对象的原型设置为另一个对象。getOwnPropertyDescriptor()
与Object.getOwnPropertyDescriptor
同样,若是在对象中存在,则返回给定的属性的属性描述符。isExtensible(target)
与Object.isExtensible
相似,判断一个对象是否可扩展(是否能够在它上面添加新的属性),它们的不一样点是,当参数不是对象时(原始值),Object
的将它强制转变为一个对象,Reflect
是直接报错。preventExtensions(target)
与Object.preventExtensions
相似,阻止新属性添加到对象,不一样点和上一条同样。apply(func, thisArg, args)
与Function.prototype.apply.call(fn, obj, args)
同样。ownKeys(target)
与Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
同样,返回一个包含全部自身属性(不包含继承属性)的数组另外一部分是将原来操做符的功能,变成函数行为。编程语言
has(target, key)
与in
操做符同样,让判断操做都变成函数行为。deleteProperty(target, key)
与delete
操做符同样,让删除操做变成函数行为,返回布尔值表明成功或失败。construct(target, argumentsList[, newTarget])
与new
操做符同样,target
构造函数,第二参数是构造函数参数类数组,第三个是new.target的值。get(target, key[, receiver])
与obj[key]
同样,第三个参数是当要取值的key
部署了getter
时,访问其函数的this
绑定为receiver
对象。set(target, key, value[, receiver])
设置target
对象的key
属性等于value
,第三个参数和set
同样。返回一个布尔值。// 老写法
'assign' in Object // true
// 新写法
Reflect.has(Object, 'assign') // true
// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1
// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1
// 旧写法
delete myObj.foo;
// 新写法
Reflect.deleteProperty(myObj, 'foo');
// new 的写法
const instance = new Greeting('张三');
// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['张三']);
// 旧写法
Object.defineProperty(MyDate, 'now', {
value: () => Date.now()
});
// 新写法
Reflect.defineProperty(MyDate, 'now', {
value: () => Date.now()
});
Reflect.get(1, 'foo') // 报错
Reflect.get(false, 'foo') // 报错
Reflect.set(1, 'foo', {}) // 报错
Reflect.set(false, 'foo', {}) // 报错
// ---------------
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
};
var myReceiverObject = {
foo: 4,
bar: 4,
};
Reflect.get(myObject, 'baz', myReceiverObject) // 8
复制代码
Proxy 对象用于定义基本操做的自定义行为(如属性查找,赋值,枚举,函数调用等),等同于在语言层面作出修改,因此属于一种“元编程”(meta programming),即对编程语言进行编程。函数
Proxy 就像在目标对象之间的一个代理,任何对目标的操做都要通过代理。代理就能够对外界的操做进行过滤和改写。post
Proxy
是构造函数,它有两个参数target
和handler
,ui
target
是用Proxy包装的目标对象(能够是任何类型的对象,包括原生数组,函数,甚至另外一个代理)。this
handler
是一个对象,其属性是当执行一个操做时定义代理的行为的函数。
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2
复制代码
Proxy
只有一个静态方法revocable(target, handler)
能够用来建立一个可撤销的代理对象。两个参数和构造函数的相同。它返回一个包含了所生成的代理对象自己以及该代理对象的撤销方法的对象。
一旦某个代理对象被撤销,它将变的几乎彻底不可用,在它身上执行任何的可代理操做都会抛出 TypeError 异常(注意,可代理操做一共有 14 种,执行这 14 种操做之外的操做不会抛出异常)。一旦被撤销,这个代理对象永远不可能恢复到原来的状态,同时和它关联的目标对象以及处理器对象将有可能被垃圾回收掉。调用撤销方法屡次将不会有任何效果,固然,也不会报错。
var revocable = Proxy.revocable({}, {
get(target, name) {
return "[[" + name + "]]";
}
});
// revocable -> {"proxy": proxy, "revoke": revoke}
var proxy = revocable.proxy;
proxy.foo; // "[[foo]]"
revocable.revoke(); // 执行撤销方法
proxy.foo; // TypeError
proxy.foo = 1 // 一样 TypeError
delete proxy.foo; // 仍是 TypeError
typeof proxy // "object",由于 typeof 不属于可代理操做
复制代码
handler
参数是代理函数对象,它一共支持 13 种拦截函数。和Reflect
的相同。若是没有定义某种操做,那么这种操做会被转发到目标对象身上。
const proxy = new Proxy({}, {
get: function(target, property, receiver) {
return receiver;
// receiver 老是指向原始的读操做所在的那个对象,通常状况下就是 Proxy 实例。
}
});
proxy.getReceiver === proxy // true
复制代码
若是一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,不然经过 Proxy 对象访问该属性会报错。
const target = Object.defineProperties({}, {
foo: {
value: 123,
writable: false,
configurable: false
},
});
const handler = {
get(target, propKey) {
return 'abc';
}
};
const proxy = new Proxy(target, handler);
proxy.foo
// TypeError: Invariant check failed
复制代码
apply
方法拦截函数的调用
、call
和apply
操做。
var target = function () { return 'I am the target'; };
var handler = {
apply: function () {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
p()
// "I am the proxy"
复制代码
defineProperty
方法拦截了Object.defineProperty
操做。
var handler = {
defineProperty (target, key, descriptor) {
return false;
}
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar' // 不会生效
// defineProperty 方法返回 false,致使添加新属性老是无效。
复制代码
注意,若是目标对象不可扩展(non-extensible),则defineProperty不能增长目标对象上不存在的属性,不然会报错。另外,若是目标对象的某个属性不可写(writable)或不可配置(configurable),则defineProperty方法不得改变这两个设置。
getPrototypeOf
方法主要用来拦截获取对象原型,会如下这些操做:
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof
ownKeys
方法用来拦截对象自身属性的读取操做,会拦截如下操做:
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for...in
经过代理,你能够轻松地验证向一个对象的传值。
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// The default behavior to store the value
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
console.log(person.age);
// 100
person.age = 'young';
// 抛出异常: Uncaught TypeError: The age is not an integer
person.age = 300;
// 抛出异常: Uncaught RangeError: The age seems invalid
复制代码
虽然 Proxy 能够代理针对目标对象的访问,但它不是目标对象的透明代理,即不作任何拦截的状况下,也没法保证与目标对象的行为一致。主要缘由就是在 Proxy 代理的状况下,目标对象内部的this
关键字会指向 Proxy 代理。
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
复制代码
const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);
proxy.getDate();
// TypeError: this is not a Date object.
// getDate 方法只能在Date对象实例上面拿到,
// 若是this不是Date对象实例就会报错。
// 这时,this绑定原始对象,就能够解决这个问题
const target = new Date('2015-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.getDate() // 1
复制代码