TypeScript计时器封装函数传参失效:内联代码可行但调用函数不行
问题描述
我正在用TypeScript开发一款计时器(Chronometer),遇到个头疼的问题:把计时逻辑封装成带参数的countHundrethsOrSeconds函数后,在setInterval里调用这个函数完全没效果;但把函数里的代码直接写到setInterval的回调里就正常运行。另外用setTimeout调用这个封装函数是没问题的,搞不懂哪里出问题了。相关代码如下:
function countHundrethsOrSeconds( countedTime: number, timeChange: number, timeGrouping: number, timeShower: string, ) { countedTime += timeChange; if (countedTime === timeGrouping) { countedTime = 0; }; if (countedTime.toString().length === 1) { chronoAndTimer[timeShower as keyof typeof chronoAndTimer].textContent = '0' + countedTime.toString(); } else { chronoAndTimer[timeShower as keyof typeof chronoAndTimer].textContent = countedTime.toString(); }; }; // some code // 这种写法不生效 secondsInterval = setInterval((): void => { countHundrethsOrSeconds(seconds, 1, 60, 'secondsShower'); }, 1000); // 这种内联写法正常工作,但我不想写重复代码 secondsInterval = setInterval((): void => { seconds += 1; if (seconds === 60) { seconds = 0; }; if (seconds.toString().length === 1) { chronoAndTimer['secondsShower'].textContent = '0' + seconds.toString(); } else { chronoAndTimer['secondsShower'].textContent = seconds.toString(); } }, 1000);
问题原因与解决方案
核心原因
TypeScript里的number是值类型,当你把外部的seconds传给函数的countedTime参数时,函数拿到的只是一个独立的副本。函数内部对countedTime的所有修改都只作用于这个副本,不会影响外部的seconds变量。而内联代码直接修改的是外部的seconds,所以能持续更新计时状态。
解决方案
这里提供两种简洁的修复方式:
方案1:让函数返回修改后的值,外部同步更新
修改函数,让它返回更新后的计时数值,然后在setInterval回调里把返回值赋值给外部的seconds:
function countHundrethsOrSeconds( countedTime: number, timeChange: number, timeGrouping: number, timeShower: string, ): number { countedTime += timeChange; if (countedTime === timeGrouping) { countedTime = 0; } // 用padStart简化补0逻辑 const displayText = countedTime.toString().padStart(2, '0'); chronoAndTimer[timeShower as keyof typeof chronoAndTimer].textContent = displayText; // 返回更新后的值 return countedTime; } // 调用时同步更新外部变量 secondsInterval = setInterval(() => { seconds = countHundrethsOrSeconds(seconds, 1, 60, 'secondsShower'); }, 1000);
方案2:用引用类型传递状态
把计时数值包裹在对象里(对象是引用类型),函数内部直接修改对象的属性,这样外部的状态会同步更新:
// 用对象包裹计时状态 const timerState = { seconds: 0 }; function countHundrethsOrSeconds( timeState: { value: number }, timeChange: number, timeGrouping: number, timeShower: string, ) { timeState.value += timeChange; if (timeState.value === timeGrouping) { timeState.value = 0; } const displayText = timeState.value.toString().padStart(2, '0'); chronoAndTimer[timeShower as keyof typeof chronoAndTimer].textContent = displayText; } // 调用时直接传状态对象 secondsInterval = setInterval(() => { countHundrethsOrSeconds(timerState, 1, 60, 'secondsShower'); }, 1000);
额外优化
代码里判断字符串长度补0的逻辑可以用padStart(2, '0')替代,一行代码搞定,更简洁可读性更强。
内容的提问来源于stack exchange,提问作者Carmelo
相关产品推荐
相关产品推荐

