Proxy是什么?
Proxy可以理解为一个代理人,他代表了一个明星在微博上与粉丝互动。实际上,这个账号是由其他人或团队运营的,他们发表的微博就代表了明星本人的意思。这里的代理人就是一个JavaScript中的Proxy对象。
JavaScript对象函数
在JavaScript中,Proxy是ES6中提供的新的API,可以用来定义对象各种基本操作的自定义行为。这些行为被称为traps(陷阱),可以理解为针对对象各种行为的钩子。当我们需要对一些对象的行为进行控制时,Proxy将变得非常有效。
Proxy的语法如下:
```javascript
let target = {};
let handlers = {}; // do nothing
let proxy = new Proxy(target, handlers);
proxy.a = 123;
console.log(target.a); // 123
```
在第二个参数为空对象的情况下,基本可以理解为是对第一个参数做的一次浅拷贝。需要注意的是,Proxy必须是浅拷贝,如果是深拷贝则会失去了代理的意义。
Traps(各种行为的代理)
在Proxy中,我们可以通过定义不同的traps来实现对对象各种行为的控制。例如:
```javascript
let obj = {
_age: 18,
get age() {
return `I'm ${this._age} years old`;
},
set age(val) {
this._age = Number(val);
}
};
console.log(obj.age); // I'm 18 years old
obj.age = 19;
console.log(obj.age); // I'm 19 years old
```
在这个例子中,我们定义了一个名为age的getter和setter,用于获取和设置对象的_age属性。当我们访问或修改obj.age时,这些getter和setter会被触发。
然而,使用gettersetterkey这种方式有一些缺点:首先,我们需要手动定义每个属性的getter和setter;其次,如果我们需要对多个属性进行类似的处理,就需要重复编写相同的代码。这时,Proxy的优势就体现出来了。
以下是重构后的内容:
## 使用Proxy解决对象属性为undefined的问题
```js
let target = { age: 18, name: 'Niko Bellic' }
let handlers = {
get (target, property) {
return `${property}: ${target[property]}`
},
set (target, property, value) {
target[property] = value
}
}
let proxy = new Proxy(target, handlers)
proxy.age = 19 // age: 19
console.log(target.age, proxy.age) // 19, 19
console.log(target.name, proxy.name) // Niko Bellic, name: Niko Bellic
```
在上述代码中,我们使用了Proxy来解决对象属性为undefined的问题。通过定义handlers对象,我们在set和get方法中对target对象进行了相应的操作。在set方法中,我们将value赋值给target[property],从而解决了属性值为undefined的问题。在get方法中,我们返回了property和对应的值,从而实现了属性值的访问。最后,我们创建了一个proxy实例,并对其进行了操作。
以下是重构后的代码,保持了原有的结构:
(() => {
let target = {};
let handlers = {
get: (target, property) => {
target[property] = (property in target) ? target[property] : {};
if (typeof target[property] === 'object') {
return new Proxy(target[property], handlers);
}
return target[property];
},
};
let proxy = new Proxy(target, handlers);
// z in proxy.x.y is false (actually this step has created an x.y property for `target`)
console.log('z' in proxy.x.y); // false
// proxy.x.y.z = 'hello'
// console.log('z' in proxy.x.y) is true
// console.log(target.x.y.z) // hello
})();
getgetkeytargetkeykeycannotgetxxxfromundefinedkeyinget普通函数与构造函数的兼容处理classES5newclassES5thisapplytrap
```javascriptclass Test {
constructor(a, b) {
console.log('constructor', a, b);
}
}
// Test(1, 2) // throw an error
let proxyClass = new Proxy(Test, {
apply: function (target, thisArg, argumentsList) {
// 如果想要禁止使用非new的方式来调用函数,直接抛出异常即可
// throw new Error(`Function ${target.name} cannot be invoked without 'new'`)
return new (target.bind(thisArg, ...argumentsList))();
},
});
proxyClass(1, 2); // constructor 1 2
applyClassapplynew
newtrapconstruct
function add(a, b) {
return a + b;
}
let proxy = new Proxy(add, {
construct: function (target, argumentsList, newTarget) {
throw new Error(`Function ${target.name} cannot be invoked with 'new'`);
},
});
proxy(1, 2); // 3 new proxy(1, 2) // throw an error
```
```javascriptlet handlers = {
get(target, property) {
if (!target.init) {
// 初始化对象
['GET', 'POST'].forEach(method => {
target[method] = (url, params = {}) => {
return fetch(url, {
headers: {
'content-type': 'application/json'
},
mode: 'cors',
credentials: 'same-origin',
method,
...params
}).then(response => response.json())
}
})
}
return target[property]
}
}
let API = new Proxy({}, handlers)
await API.GET('XXX')
await API.POST('XXX', { body: JSON.stringify({ name: 1 }) })
```
Proxy 是 ES6 中提供的新的 API,可以用来定义对象各种基本操作的自定义行为。Proxy 可以拦截 JS 引擎内部目标的底层对象操作,这些底层操作被拦截后会触发响应特定操作的陷阱函数(traps),对于别人封装好的对象或内建对象,都可以自定义操作。
下面是一个使用 Proxy 自定义 trap 的例子:
```javascript
let assert = new Proxy({}, { set (target, message, value) { if (!value) console.error(message) } });
assert['Isn\\'t true'] = false; // Error: Isn\\'t true
assert['Less than 18'] = 18 >= 19; // Error: Less than 18
```