JS对函数实现get和apply的拦截代理,调用call时会发生什么?
问题描述
现有一个名为Animal
的函数,它会打印出相关语句。然后使用代理,实现其中的get
和apply
方法:
function Animal() {
console.log("This is a " + this.species);
}
var animalProxy = new Proxy(Animal, {
get: function(target, prop, recevier) {
console.log("Proxy get was invoked!");
return target[prop];
},
apply: function(target, context, args) {
console.log("Proxy apply was invoked!");
return Reflect.apply(...arguments);
}
});
animalProxy
实现了对函数Animal
的代理,现在,有如下语句
console.log( animalProxy.call({
species: "Cat"
} ));
它的运行结果也如预期,输出了
Proxy get was invoked!
Proxy apply was invoked!
This is a Cat
undefined
问题
- 发现对代理对象调用
call
方法时,get
和apply
两个拦截方法都被调用了。但是注意到get
方法中返回的是target[prop]
,在这里我认为返回的应该是原对象(即Animal
)的call方法,和代理对象无关。既然已经返回了和代理对象无关的方法,这么说来后续对call
进行函数调用也不是在代理对象上调用的,为什么还会触发apply
拦截呢? - 假设,对于代理对象使用
call
、apply
的行为都会被apply
拦截,那么看起来get
方法返回的值就不重要了,因为它的返回值不影响apply
的使用。但是如果get
方法返回一个非函数对象,例如返回1
,那么运行时会触发一个错误:TypeError: animalProxy.call is not a function
。
延伸问题
假如我在每个拦截方法中都输出arguments
参数列表,即:
var animalProxy = new Proxy(Animal, {
get: function(target, prop, recevier) {
console.log("Proxy get was invoked!",arguments); //输出参数列表
return target[prop];
},
apply: function(target, context, args) {
console.log("Proxy apply was invoked!",arguments); //输出参数列表
return Reflect.apply(...arguments);
}
});
那么使用console.log( animalProxy.call({species: "Cat" } ));
语句时,在Chrome控制台环境下运行正常,但是在Nodejs(v10.6.0)中会爆栈,它会在get
中的console
语句中发生栈溢出:
< console.log("Proxy get was invoked!",arguments);
< ^
< RangeError: Maximum call stack size exceeded
然而如果注释掉get
中的arguments
但是保留apply
中的arguments
,那么在Nodejs(v10.6.0)环境中就运行正常。
这是为什么呢?是和Nodejs版本问题有关吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题 1:
你在
get
中返回的是只是call
函数,他的调用者还是proxy
,所以还会触发apply
你可以用下面的例子运行试试看
add
会输出 3 次get
,也就是在add
函数内部this
指向应该还是proxy
用箭头函数的
minus
就只会执行一次,因为箭头函数默认绑定外层的this
,不会因为调用者而改变,所以不会触发get
问题 2:
animalProxy.call({ species: 'Cat' })
这句语句要分为两步来看,
第一步是
.
操作,也就是get
操作,触发get
拦截,第二步是调用
get
返回的call
,触发apply
第二步的
call
执行的是get
返回的函数,所以如果返回一个非函数,却要执行肯定会报错的你可以直接返回一个函数,这样执行的时候就不会报错,但不会执行
apply
拦截补充问题
爆栈是因为
arguments
里面的第三个参数recevier
造成的,这个参数指向的是代理本身用
cosole.log
输出的时候,chrome
和node
实现的好像不太一样chrome
会直接输出代理本身而
node
找到proxy
所代理的对象obj
,而在找obj
的过程中会不断触发get
,然后一直循环所以
chrome
可以正常运行,而node
会爆栈这个问题的话,你可以用我第一个例子试试看,在
add
函数里面加一句然后在
chrome
和node
里面分别运行看看在
chrome
里面会直接输出proxy
,get
只触发 3 次,在ndoe
里面会输出obj
,get
触发了 8 次