JavaScript装饰器中函数表达式与函数声明的行为差异问题
函数创建方式导致装饰器计数器共享的原因分析
问题的核心差异在于变量作用域与声明方式:
1. 未声明的函数表达式导致全局变量污染
第一个代码中,wrapper = function(...args)没有使用var/let/const声明变量,这会让wrapper自动成为全局变量(浏览器环境挂载到window,Node.js环境挂载到global)。
执行流程拆解:
- 第一次调用
counter(sum):
全局变量wrapper被赋值为第一个包装函数,同时给它添加counter = 0属性,最后将这个函数返回赋值给a。 - 第二次调用
counter(mul):
全局变量wrapper被重新赋值为第二个包装函数,同时重置它的counter = 0,返回后赋值给b。 - 调用
a()或b()时:
函数内部的wrapper.counter++实际操作的是全局的wrapper变量(也就是最后一次赋值给b的那个函数)。而a指向的第一个包装函数的counter属性从未被修改过,始终是初始的0;所有调用次数都累加给了b对应的全局wrapper,最终出现a.counter=0、b.counter=4的结果。
2. 函数声明的局部作用域隔离
第二个代码中,function wrapper(...args)是函数声明,它会被绑定在counter函数的局部作用域内:
- 每次调用
counter都会创建一个独立的局部作用域,每个作用域内的wrapper都是全新的函数实例。 - 每个
wrapper的counter属性都是独立的,分别属于a和b指向的包装函数,因此调用次数会正确统计,不会互相干扰。
修正方案(函数表达式的正确写法)
如果要使用函数表达式,只需给wrapper添加局部变量声明即可,避免全局污染:
function counter(func) { // 使用let声明局部变量wrapper let wrapper = function(...args) { wrapper.counter++; let result = func.apply(null, args); return result; } wrapper.counter = 0; return wrapper; }
内容的提问来源于stack exchange,提问作者Иван Бельков
相关产品推荐
相关产品推荐

