1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 02:02:21 +00:00

docs(proxy): edit proxy

This commit is contained in:
ruanyf 2016-12-24 20:56:36 +08:00
parent c7d9c5d138
commit 04f582da78
3 changed files with 74 additions and 6 deletions

View File

@ -317,6 +317,29 @@ const el = dom.div({},
document.body.appendChild(el);
```
如果一个属性不可配置configurable和不可写writable则该属性不能被代理通过 Proxy 对象访问该属性会报错。
```javascript
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
```
### set()
`set`方法用来拦截某个属性的赋值操作。
@ -380,6 +403,8 @@ proxy._prop = 'c'
上面代码中,只要读写的属性名的第一个字符是下划线,一律抛错,从而达到禁止读写内部属性的目的。
注意,如果目标对象自身的某个属性,不可写也不可配置,那么`set`不得改变这个属性的值,只能返回同样的值,否则报错。
### apply()
`apply`方法拦截函数的调用、`call``apply`操作。
@ -474,7 +499,7 @@ var p = new Proxy(obj, {
'a' in p // TypeError is thrown
```
上面代码中,`obj`对象禁止扩展,结果使用`has`拦截就会报错。
上面代码中,`obj`对象禁止扩展,结果使用`has`拦截就会报错。也就是说,如果某个属性不可配置(或者目标对象不可扩展),则`has`方法就不得“隐藏”(即返回`false`)目标对象的该属性。
值得注意的是,`has`方法拦截的是`HasProperty`操作,而不是`HasOwnProperty`操作,即`has`方法不判断一个属性是对象自身的属性,还是继承的属性。
@ -588,6 +613,8 @@ delete proxy._prop
上面代码中,`deleteProperty`方法拦截了`delete`操作符,删除第一个字符为下划线的属性会报错。
注意目标对象自身的不可配置configurable的属性不能被`deleteProperty`方法删除,否则报错。
### defineProperty()
`defineProperty`方法拦截了`Object.defineProperty`操作。
@ -606,6 +633,8 @@ proxy.foo = 'bar'
上面代码中,`defineProperty`方法返回`false`,导致添加新属性会抛出错误。
注意如果目标对象不可扩展extensible`defineProperty`不能增加目标对象上不存在的属性否则会报错。另外如果目标对象的某个属性不可写writable或不可配置configurable`defineProperty`方法不得改变这两个设置。
### getOwnPropertyDescriptor()
`getOwnPropertyDescriptor`方法拦截`Object.getOwnPropertyDescriptor`,返回一个属性描述对象或者`undefined`
@ -655,6 +684,8 @@ Object.getPrototypeOf(p) === proto // true
上面代码中,`getPrototypeOf`方法拦截`Object.getPrototypeOf()`,返回`proto`对象。
注意,`getPrototypeOf`方法的返回值必须是对象或者`null`否则报错。另外如果目标对象不可扩展extensible `getPrototypeOf`方法必须返回目标对象的原型对象。
### isExtensible()
`isExtensible`方法拦截`Object.isExtensible`操作。
@ -674,7 +705,9 @@ Object.isExtensible(p)
上面代码设置了`isExtensible`方法,在调用`Object.isExtensible`时会输出`called`
这个方法有一个强限制,如果不能满足下面的条件,就会抛出错误。
注意,该方法只能返回布尔值,否则返回值会被自动转为布尔值。
这个方法有一个强限制,它的返回值必须与目标对象的`isExtensible`属性保持一致,否则就会抛出错误。
```javascript
Object.isExtensible(proxy) === Object.isExtensible(target)
@ -855,9 +888,9 @@ Object.getOwnPropertyNames(p)
### preventExtensions()
`preventExtensions`方法拦截`Object.preventExtensions()`。该方法必须返回一个布尔值。
`preventExtensions`方法拦截`Object.preventExtensions()`。该方法必须返回一个布尔值,否则会被自动转为布尔值
这个方法有一个限制,只有`Object.isExtensible(proxy)``false`(即不可扩展`proxy.preventExtensions`才能返回`true`,否则会报错。
这个方法有一个限制,只有目标对象不可扩展时(即`Object.isExtensible(proxy)``false``proxy.preventExtensions`才能返回`true`,否则会报错。
```javascript
var p = new Proxy({}, {
@ -876,7 +909,7 @@ Object.preventExtensions(p) // 报错
```javascript
var p = new Proxy({}, {
preventExtensions: function(target) {
console.log("called");
console.log('called');
Object.preventExtensions(target);
return true;
}
@ -908,6 +941,8 @@ proxy.setPrototypeOf(proxy, proto);
上面代码中,只要修改`target`的原型对象,就会报错。
注意该方法只能返回布尔值否则会被自动转为布尔值。另外如果目标对象不可扩展extensible`setPrototypeOf`方法不得改变目标对象的原型。
## Proxy.revocable()
`Proxy.revocable`方法返回一个可取消的 Proxy 实例。
@ -927,6 +962,8 @@ proxy.foo // TypeError: Revoked
`Proxy.revocable`方法返回一个对象,该对象的`proxy`属性是`Proxy`实例,`revoke`属性是一个函数,可以取消`Proxy`实例。上面代码中,当执行`revoke`函数之后,再访问`Proxy`实例,就会抛出一个错误。
`Proxy.revocable`的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
## this 问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的`this`关键字会指向 Proxy 代理。
@ -998,3 +1035,32 @@ const proxy = new Proxy(target, handler);
proxy.getDate() // 1
```
## 应用实例
### Web 服务器的客户端
Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。
```javascript
const service = createWebService('http://example.com/data');
service.employees().then(json => {
const employees = JSON.parse(json);
// ···
});
```
上面代码新建了一个 Web 服务的接口这个接口返回各种数据。Proxy 可以拦截这个对象的任意属性,所以不用为每一种数据写一个适配方法,只要写一个 Proxy 拦截就可以了。
```javascript
function createWebService(baseUrl) {
return new Proxy({}, {
get(target, propKey, receiver) {
return () => httpGet(baseUrl+'/' + propKey);
}
});
}
```
同理Proxy 也可以用来实现数据库的 ORM 层。

View File

@ -225,6 +225,8 @@ delete myObj.foo;
Reflect.deleteProperty(myObj, 'foo');
```
该方法返回一个布尔值。如果删除成功,或者被删除的属性不存在,返回`true`;删除失败,被删除的属性依然存在,返回`false`
### Reflect.construct(target, args)
`Reflect.construct`方法等同于`new target(...args)`,这提供了一种不使用`new`,来调用构造函数的方法。

View File

@ -139,7 +139,7 @@ function init_back_to_top_button() {
function goTop(e) {
if(e) e.preventDefault();
$('html body').animate({
$('html, body').animate({
scrollTop: 0
}, 200);
history.pushState(null, null, '#' + location.hash.split('#')[1]);