Proxy
Proxy()
构造器用来创建 Proxy
对象。
其语法为
js
const p = new Proxy(target, handler)
参数
- target:为目标对象
- handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理
p
的行为
handler
proxy.apply()
handler.apply()
方法用于拦截函数的调用
函数也是一个对象,Proxy一样可以代理函数,当函数对象被调用时,此方法被调用
如下代码
js
function sum(a, b) {
return a + b;
}
const handler = {
apply: function (target, thisArg, argumentsList) {
console.log(`sum: ${argumentsList}`);
// 输出:"sum: 1,2"
return target(argumentsList[0], argumentsList[1]) * 10; //结果乘 10
},
};
const proxy1 = new Proxy(sum, handler);
console.log(sum(1, 2)); //直接调用此函数时 输出:3
console.log(proxy1(1, 2)); //调用proxy代理的函数时 输出:30
参数
- target :目标对象(函数)。在上面是
sum
函数 - thisArg :被调用时的上下文对象。就是调用函数时的
this
值。 - argumentsList:被调用时的参数数组。
返回值
对于返回值来说,aplly函数可以返回任何值
但是得返回函数操作,即是要调用目标函数,如果你想要目标函数执行的话
如下代码
js
const targetFunction = function (a, b) {
return a + b;
};
const proxy = new Proxy(targetFunction, {
apply(target, thisArg, argumentsList) {
console.log('函数调用');
console.log(`传递的参数:${argumentsList}`); //输出:传递的参数:2,3
return target.apply(thisArg, argumentsList)*10;
}
});
console.log(proxy(2, 3)); //输出:50
const targetFunction = function (a, b) {
return a + b;
};
const proxy = new Proxy(targetFunction, {
apply(target, thisArg, argumentsList) {
console.log('函数调用');
console.log(`传递的参数:${argumentsList}`); //输出:传递的参数:2,3
return 1; //返回 1
}
});
console.log(proxy(2, 3)); //不管函数参数怎么样 都是输出:1
拦截操作
-
proxy(...args)
上面已经有的调用方式,就不多说了
-
Function.prototype.apply() 和 Function.prototype.call()
有以下例子
jsconst targetFunction = function (a, b) { return a + b; }; const proxy = new Proxy(targetFunction, { apply(target, thisArg, argumentsList) { console.log('拦截目标函数的调用'); console.log(`传递的参数:${argumentsList}`); return target.apply(thisArg, argumentsList); } }); proxy.apply(null, [2, 3]); // 输出: 拦截目标函数的调用 传递的参数:2,3 //这个apply是改变函数this指向的方法,不是proxy上的 const targetFunction = function (a, b) { return a + b; }; const proxy = new Proxy(targetFunction, { apply(target, thisArg, argumentsList) { console.log('拦截目标函数的调用'); console.log(`传递的参数:${argumentsList}`); return target.apply(thisArg, argumentsList); } }); proxy.call(null, 2, 3); // 输出: 拦截目标函数的调用 传递的参数:2,3 //call也是改变函数this指向并且执行的
-
Reflect.apply()
jsReflect.apply(targetFunction, thisArg, argumentsList) //这个也是改变this指向JavaScript 中的一个静态方法
proxy.construct()
handler.construct()
方法用于拦截 new
操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target
必须是有效的)。
就是对于普通对象不生效(得构造函数)
看如下例子代码
js
function monster1(disposition) {
this.disposition = disposition;
}
const handler1 = {
construct(target, args) {
console.log(`Creating a ${target.name}`); //当new proxy1时触发
// Creating a monster1
return new target(...args);
},
};
const proxy1 = new Proxy(monster1, handler1);
console.log(new proxy1('fierce').disposition); //上面输出 Creating a monster1
//这里输出 fierce
其语法
js
var p = new Proxy(target, {
construct: function (target, argumentsList, newTarget) {},
});
参数
- target:目标对象
- argmentsList:
constructor
的参数列表 - newTarget:最初被调用的构造函数,就上面的例子而言是 p
返回值
construct必须返回一个对象!!!否则将会抛出错误
拦截操作
下面是演示如何拦截new
操作
js
var p = new Proxy(function () {}, {
construct: function (target, argumentsList, newTarget) {
console.log("called: " + argumentsList.join(", "));
return { value: argumentsList[0] * 10 };
},
});
console.log(new p(1).value); // "called: 1"; 输出:10
handler.get()
handler.get()
方法用于拦截对象的读取属性操作。
说白了就是会拦截读取对象里面值的函数
其语法是
js
var p = new Proxy(target, {
get: function (target, property, receiver) {},
});
参数
- **target:**目标对象
- property:被获取的属性名
- receiver:
Proxy
或者继承Proxy
的对象
返回值
get
方法可以返回任何值
拦截操作
- 访问属性:
proxy[foo] 和
proxy.bar
- 访问原型链上的属性:
Object.create(proxy)[foo]
- Reflect.get()
如果违背了以下的约束,proxy
会抛出 TypeError
- 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同。
- 如果要访问的目标属性没有配置访问方法,即 get 方法是
undefined
的,则返回值必须为undefined
。
如下就会抛出错误
js
var obj = {};
Object.defineProperty(obj, "a", {
configurable: false, //不可配置
enumerable: false,
value: 10,
writable: false, //不可写
});
var p = new Proxy(obj, {
get: function (target, prop) {
return 20;
},
});
p.a; //会抛出 TypeError
handler.set()
handler.set()
方法是设置属性值操作的捕获器。
看如下代码
js
const monster1 = { eyeCount: 4 };
const handler1 = {
set(obj, prop, value) {
if (prop === 'eyeCount' && value % 2 !== 0) {
console.log('Monsters must have an even number of eyes');
} else {
return Reflect.set(...arguments);
}
},
};
const proxy1 = new Proxy(monster1, handler1);
proxy1.eyeCount = 1;
//输出 "Monsters must have an even number of eyes"
console.log(proxy1.eyeCount);
// 输出 4
proxy1.eyeCount = 2;
console.log(proxy1.eyeCount);
// 输出 2
参数
- target:目标对象
- property:将被设置的属性名或 Symbol
- value:新属性值
- receiver:最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身
返回值
set()
方法应当返回一个布尔值。
- 返回
true
代表属性设置成功。(返回与否,只要修改了照样可以) - 在严格模式下,如果
set()
方法返回false
,那么会抛出一个TypeError
异常。
拦截操作
- 指定属性值:
proxy[foo] = bar
和proxy.foo = bar
- 指定继承者的属性值:
Object.create(proxy)[foo] = bar
Reflect.set()
在以下情况下程序报错
- 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。
- 如果目标属性没有配置存储方法,即
[[Set]]
属性的是undefined
,则不能设置它的值。 - 在严格模式下,如果
set()
方法返回false
,那么也会抛出一个TypeError
异常。
handler.has()
handler.has()
方法是针对 in
操作符的代理方法。
如下代码示例
js
const handler1 = {
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
},
};
const monster1 = {
_secret: 'easily scared',
eyeCount: 4,
};
const proxy1 = new Proxy(monster1, handler1);
console.log('eyeCount' in proxy1);
// 输出 true
console.log('_secret' in proxy1);
// 输出 false //
console.log('_secret' in monster1);
// 输出 true
当我们使用 in
操作符的时候就会触发此操作
语法如下
js
var p = new Proxy(target, {
has: function (target, prop) {},
});
参数
- target:目标对象
- 目标对象
返回值
has
方法返回一个 boolean 属性的值。
拦截操作
- 属性查询:
foo in proxy
- 继承属性查询:
foo in Object.create(proxy)
with
检查: with(proxy) { (foo); }
Reflect.has()
该方法不可用时是
-
在 JavaScript 中,当目标对象的某个属性本身被设置为不可配置 (
configurable: false
) 时,这意味着这个属性的配置不可被改变,无法通过代理隐藏这个属性。看如下代码
js// 创建一个对象并定义一个属性,将其设置为不可配置 var obj = { name: 'John' }; Object.defineProperty(obj, 'name', { configurable: false // 设置为不可配置 }); // 使用代理拦截 'has' 方法 var proxyObj = new Proxy(obj, { has: function(target, prop) { if (prop === 'name') { return false; // 在这里希望代理隐藏属性 } else { return prop in target; } } }); console.log('name' in proxyObj); // 输出还是"true",这就是无法通过代理隐藏不可配置的属性
-
当目标对象被设置为不可扩展 (
Object.preventExtensions(obj)
) 时,意味着无法向该对象添加新属性。这种情况下,即使通过代理也无法隐藏该对象原有的属性。看如下代码
js// 创建一个不可扩展的对象 var obj = { name: 'John' }; Object.preventExtensions(obj); // 尝试通过代理隐藏属性 var proxyObj = new Proxy(obj, { has: function(target, prop) { return false; // 代理隐藏属性,但是无法隐藏原始对象的属性 } }); console.log('name' in proxyObj); // 输出 true,代理无法隐藏不可扩展对象的属性
这个对象里的属性不可被代理隐藏
handler.ownKeys()
handler.ownKeys()
方法用于拦截 Reflect.ownKeys()
对于这个属性我们需要了解以下什么才是Reflect.ownKeys()
静态方法 Reflect.ownKeys()
返回一个由目标对象自身的属性键组成的数组
换句话说他就是可以返回目标对象的自身属性组成的数组
注意了是自身的!!!
我们看一看它的示例代码
js
Reflect.ownKeys({ z: 3, y: 2, x: 1 }); // [ "z", "y", "x" ]
Reflect.ownKeys([]); // ["length"] //空数组自带length属性
var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {
[sym]: 0,
str: 0,
773: 0,
0: 0,
[sym2]: 0,
"-1": 0,
8: 0,
"second str": 0,
};
Reflect.ownKeys(obj);
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
// Indexes in numeric order, 数值类型排前面
// strings in insertion order, 字符串中间
// symbols in insertion order Symbol后面
我们回到主题 **handler.ownKeys()
**拦截 Reflect.ownKeys()
那它是怎么样拦截的。我们看一看如下代码
js
const monster1 = {
_age: 111,
[Symbol('secret')]: 'I am scared!',
eyeCount: 4,
};
const handler1 = {
ownKeys(target) {
return Reflect.ownKeys(target);
},
};
const proxy1 = new Proxy(monster1, handler1);
for (const key of Object.keys(proxy1)) { //也可以拦截 Object.keys
console.log(key);
//输出 "_age"
// 输出 "eyeCount"
}
参数
只有 target:目标对象
返回值
ownKeys方法必须返回一个数组
拦截操作
但是ownKeys只能符合以下情况下不会报错