ES6 学习笔记(十二)代理器Proxy的简单使用
1、前言
以前在学习react时做了个仿手机端的QQ音乐项目。当时的数据是通过proxy代理的QQ音乐数据接口,直接写在package.json里面。Proxy 对象(Proxy)是 ES6的特性,只是不太常用。
2、基本内容
2.1 含义
正如MDN上所说,Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。 通俗的讲,Proxy是目标对象的一个代理器。即:在目标对象之前架设一层“拦截”,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
2.2 语法
// 直接通过new Proxy实例一个代理对象
const p = new Proxy(target, handler)
其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来表示拦截行为。
常见的拦截项属性:
属性 | 作用 |
---|---|
has | 拦截 in 操作。比如,你可以用它来隐藏对象上某些属性。 |
get | 来拦截读取操作。比如当试图读取不存在的属性时,你可以用它来返回默认值 |
set | 用来拦截赋值操作。比如给属性赋值的时候你可以增加验证的逻辑,如果验证不通过可以抛出错误。 |
apply | 用来拦截函数调用操作。比如,你可以把所有的函数调用都包裹在 try/catch 语句块中 |
ownKeys | 用于拦截Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法 |
更多的拦截项见MDN
举个例子:
let obj = new Proxy(
{},
{
// 对属性的获取进行拦截
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}`);
return Reflect.get(target, propKey, receiver)
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}`);
return Reflect.set(target, propKey, value, receiver)
}
}
)
obj.count = 1; // 对空对象的count属性进行操作,调用的set方法进行赋值
++obj.count;
console.log(obj);
输出结果: setting count getting count setting count { count: 2 }
注意: 要使Proxy起作用,就要对Proxy实例进行操作而不是针对目的对象进行操作,如:
// 代理包围需要代理的对象
let p = new Proxy(
{},
{
get(target, propKey) {
return 22
}
}
)
console.log(p.name);
console.log(p.age);
输出结果: 22 22
// 需要代理的对象的反包围(委托)
let obj = {
proxy: new Proxy(this, {
get(target, prop) {
console.log(`getting ${prop}`);
return target[prop] ?? "Not Found"
}
})
}
let x = obj.proxy.a
console.log(x);
输出结果: getting a Not Found
2.3 实例
2.3.1 实例1 对象属性的拦截
同一个拦截器对象,可以设置拦截多个操作,如:
let handler = {
get(target, name) {
if (name == "prototype") {
return Object.prototype;
}
return `Hello,${name}`
},
apply(target, thisBinding, args) {
return args[0]
},
construct(target, args) {
let obj = new target(...args)
obj.name = "foo"
obj.value = args[1]
return obj
}
}
let fproxy = new Proxy(function (x, y) {
return x + y
}, handler)
console.log(fproxy(1, 2));
console.log(new fproxy(1, 2));
console.log(fproxy.prototype == Object.prototype);
console.log(fproxy.foo);
输出结果: 1 { name: 'foo', value: 2 } true Hello,foo
2.3.2 实例2 实现数组或对象通过负索引值获取相应值
function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey)
if (index < 0) {
propKey = String(target.length + index)
}
return Reflect.get(target, propKey, receiver)
}
}
let target = []
target.push(...elements)
return new Proxy(target, handler)
}
let arr = createArray("a", "b", "c")
let obj = createArray({
name: "Tom",
age: 22
})
console.log(arr[-1]);
console.log(obj[-1]);
输出结果: c { name: 'Tom', age: 22 }
get的receiver参数,总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例
2.3.3 实例3 实现生成 DOM 节点的通用函数dom
dom.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>dom</title>
</head>
<body>
</body>
<script src="dom.js"></script>
</html>
dom.js
const dom = new Proxy({}, {
get(target, property) {
return function (attrs = {}, ...children) {
const el = document.createElement(property);
for (let prop of Object.keys(attrs)) {
el.setAttribute(prop, attrs[prop]);
}
for (let child of children) {
if (typeof child === "string") {
child = document.createTextNode(child);
}
el.appendChild(child);
}
return el;
}
}
})
const el = dom.div({},
"Hello, my blog is ",
dom.a({ href: "https://blog.csdn.net/Iamflame" }, "here"),
". I like:",
dom.ul(
{},
dom.li({}, "html5"),
dom.li({}, "css3"),
dom.li({}, "js"),
dom.li({}, "vue"),
dom.li({}, "node")
)
);
document.body.appendChild(el);
运行结果: