JS全球"让"变量没有在函数中更新?

时间:2016-11-22 01:18:50

标签: javascript google-chrome ecmascript-6 requestanimationframe let

编辑:我已将此报告为Chromium错误:https://bugs.chromium.org/p/chromium/issues/detail?id=668257

我在JS中用可以射击的敌人创建一个小帆布游戏。为了测试,我创建了一个标记,全局声明为let fancy = true;,以确定是否使用" fancy"定位算法。我这样按下P就会切换这个标志。我的主要功能frame每秒调用另一个函数autoShoot五次。 autoShoot使用fancy标记。

今天,奇怪的事情开始发生了;我不记得引入了什么变化。有时候,当我按下P时,autoShoot就像fancy这样的行为没有被切换。我做了一些调试,发现新的切换值 反映在frame内,但在autoShoot中,值未更新。它间歇性地发生,有时autoShoot中的值会自行修复(没有我做过任何事情)。

我已将代码缩减为以下内容,但仍然存在问题。尝试多次按P。对我来说,这两个值不同步"按P一次或两次后显示不同:

Screenshot after pressing P on my computer

(我正在运行Chrome"版本54.0.2840.99 m"在Windows 10上。)



const canvas = document.getElementById("c");
const width = 0;
const height = 0;
const ctx = canvas.getContext("2d");
const ratio =1;// (window.devicePixelyRatio||1)/(ctxFOOOOOOOOFOOOOOOOOOFOOOOO||1);
canvas.width = width*ratio;
canvas.height = height*ratio;
canvas.style.width = width+"px";
canvas.style.height = height+"px";
ctx.scale(ratio, ratio);

function testSet(id, val) {
  console.log(id+": "+val);
  document.getElementById(id).innerText = val;
}


let fancy = true;
document.body.addEventListener("keydown", function(e) {
  if (e.keyCode == 80) {
    fancy = !fancy;
    console.log("Set fancy to: "+fancy);
  }
});

let bullets = Array(2000);
let lastTime = 0, shotTimer = 0;
function frame(time) {
  const dt = (time - lastTime)/1000;
  lastTime = time;
  
  if ((shotTimer -= dt) <= 0) {
    testSet("frame", fancy);
    autoShoot();
    shotTimer = 0.2;
  }
  for (let b of bullets) {}
  
  requestAnimationFrame(frame);
}
function autoShoot() {
  testSet("autoShoot", fancy);
}

requestAnimationFrame(frame);
&#13;
<code>
  fancy (frame)     = <span id="frame"></span><br>
  fancy (autoShoot) = <span id="autoShoot"></span>
</code>
<canvas id="c"></canvas>
&#13;
&#13;
&#13;

到处玩,这里有一些观察:

  • 删除以下任何一项导致问题消失:
    • 顶部代码中处理画布的任何行,甚至 只是const ratio 之后的评论
    • for循环的空:for (let b of bullets) {}
    • let fancy =更改为var fancy =或仅fancy =
    • 将整个事物放在全局范围之外(通过使用IIFE,onload处理程序或块范围)
  • 增加bullets数组的大小会增加问题发生的频率。我认为这是因为它使frame需要更长的时间来执行;最初,bullets.length只有20,但每次循环迭代都做了一些事情来更新子弹等。

这是否会在您的计算机上发生?这有什么合理的解释吗?我尝试重新启动浏览器,没有变化。

2 个答案:

答案 0 :(得分:2)

每个人都评论说它似乎是一个Chrome问题。

我已尝试在Chrome版本45.0.2454.85 m (64-bit)44.0.2403.107 m (32-bit)上重现相同的问题(当然是启用严格模式),但我没有成功。但是在版本54.0.2840.99 m (64-bit)上它就在那里。

我注意到将requestAnimationFrame更改为setInterval之类的内容也会让问题彻底消失。

因此,我认为这种奇怪的行为与较新版本的Chrome's requestAnimationFrame有关,并且可能会阻止let的范围界定和函数提升。

我无法说出Chrome的哪个版本我们可以看到这种&#34;错误&#34;,但我可以假设它可以是版本52,因为在此版本中发生了许多更改,例如Garbage collection的新方法,对es6es7等的原生支持。有关详细信息,您可以观看this video from I/O 2016

也许,新的Garbage collection方法导致了这个问题,因为正如他们在上面提到的视频中所说的那样,它与浏览器框架相连,类似于v8的GC会浏览器处于空闲状态,以便不触及帧的绘制等。正如我们所知requestAnimationFrame是一种在下一帧绘图上调用callback的方法,也许在这个过程中这些奇怪的事情和#39;发生了。但这只是一个假设,而我没有能力对此说些严肃的事情:)

答案 1 :(得分:0)

我在Mac上使用Chrome 54.0.2840.98,它也会发生。我认为这是一个范围问题,因为如果我将let语句后面的声明包装到{…}块中,那么该代码段工作正常,并且在按下键后两个值都会立即更改。

&#13;
&#13;
const canvas = document.getElementById("c");
const width = 0;
const height = 0;
const ctx = canvas.getContext("2d");
const ratio =1;// (window.devicePixelyRatio||1)/(ctxFOOOOOOOOFOOOOOOOOOFOOOOO||1);
canvas.width = width*ratio;
canvas.height = height*ratio;
canvas.style.width = width+"px";
canvas.style.height = height+"px";
ctx.scale(ratio, ratio);

function testSet(id, val) {
  console.log(id+": "+val);
  document.getElementById(id).innerText = val;
}


let fancy = true;
{
  document.body.addEventListener("keydown", function(e) {
    if (e.keyCode == 80) {
      fancy = !fancy;
      console.log("Set fancy to: "+fancy);
    }
  });

  let bullets = Array(2000);
  let lastTime = 0, shotTimer = 0;
  function frame(time) {
    const dt = (time - lastTime)/1000;
    lastTime = time;
  
    if ((shotTimer -= dt) <= 0) {
      testSet("frame", fancy);
      autoShoot();
      shotTimer = 0.2;
    }
    for (let b of bullets) {}
  
    requestAnimationFrame(frame);
  }
  function autoShoot() {
    testSet("autoShoot", fancy);
  }

  requestAnimationFrame(frame);
}
&#13;
<code>
  fancy (frame)     = <span id="frame"></span><br>
  fancy (autoShoot) = <span id="autoShoot"></span>
</code>
<canvas id="c"></canvas>
&#13;
&#13;
&#13;