JavaScript setTimeout作用域报错:读取classList时无法访问null属性
问题原因
这个报错的核心是setTimeout的异步执行特性导致的,代码执行顺序和你预期的不一致:
- 当你翻开第二张卡片、走到不匹配的分支时,你只是定义了
setTimeout的回调函数,这个函数不会立刻执行,会被浏览器挂起,等到2秒后才会触发 - 定义完
setTimeout之后,你立刻同步执行了firstCard = null; secondCard = null;,这时候两个全局变量就已经被清空了 - 等2秒后回调函数执行时,读取的
firstCard和secondCard早就变成了null,自然就报找不到classList的错误了
修复方案
你可以把两个卡片的引用暂存到局部变量里,或者把重置变量的逻辑放到合适的位置,同时建议加一个锁避免用户在2秒等待期点击其他卡片产生逻辑混乱,修正后的代码如下:
let firstCard = null; let secondCard = null; let points = 0; let turnedCards = 0; // 新增等待锁,避免等待期间用户重复点击 let isWaiting = false; const createCard = (cityName) => { const card = document.createElement('div'); card.classList.add('card'); card.innerText = cityName; card.addEventListener('click', () => { // 等待期间、已翻开/已匹配的卡片禁止重复点击 if (isWaiting || card.classList.contains('revealed') || card.classList.contains('final')) { return; } if (firstCard == null) { firstCard = card; card.classList.add('revealed'); } else if (secondCard == null) { secondCard = card; card.classList.add('revealed'); if (firstCard.innerText == secondCard.innerText && firstCard != secondCard && !firstCard.classList.contains('final') && !secondCard.classList.contains('final')) { updateScore(); firstCard.classList.add('final'); secondCard.classList.add('final'); // 匹配成功直接重置 firstCard = null; secondCard = null; } else { // 暂存当前两张卡片的引用,不受外部变量重置影响 const cardA = firstCard; const cardB = secondCard; isWaiting = true; setTimeout(() => { if (!cardA.classList.contains('final')) { cardA.classList.remove('revealed'); } if (!cardB.classList.contains('final')) { cardB.classList.remove('revealed'); } // 回调执行完再重置变量和锁 firstCard = null; secondCard = null; isWaiting = false; },2000); } } }); gameArea.appendChild(card); }
补充说明
除了修复空指针报错外,新增的锁逻辑还能避免用户在2秒等待期连续点击多张卡片,导致卡片状态混乱的问题,更符合配对游戏的常规交互逻辑。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

