需要 Konami Key Sequence Array.splice() 解释

问题描述

我正在用 JavaScript 做 Konami Code 练习,虽然我自己让它工作,但答案对我来说毫无意义。有人愿意解释吗?

我的解决方案:

const pressed = [];
var secretCode = 'wesbos';
window.addEventListener('keyup',e => {

  //My code   
  if (pressed.length < 6) {
    pressed.push(e.key)
  } else if (pressed.length === 6) {
    pressed.shift()
    pressed.push(e.key)
    console.log(pressed)
  }
  //End my code

  if (pressed.join('').toLowerCase() === secretCode) {
    console.log("SECRET COMMAND ACTION CODE TRIGGERED! COMMENCE KAMEHAMEHA");    
    $.getScript('http://www.cornify.com/js/cornify.js',function() {
      cornify_add();
      $(document).keydown(cornify_add);
    });
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

https://medium.com/@nikkoortega/key-sequence-detection-f90773e3aa60 的回答,我不明白:

const pressed = [];
const secretCode = 'wesbos';
window.addEventListener('keyup',e => {
  
  //Answer code
  pressed.push(e.key)
  pressed.splice(-secretCode.length - 1,pressed.length - secretCode.length)
  //End answer code

  if (pressed.join('').toLowerCase() === secretCode) {
    console.log("SECRET COMMAND ACTION CODE TRIGGERED! COMMENCE KAMEHAMEHA");
  }
})

解决方法

代码的重点是创建一个 queue,一个 FIFO 结构。

Array#splice 是一个令人困惑的就地函数,它删除从第一个参数到第二个参数的元素,并使用负索引包装。第三个参数可选地添加新元素,但此处未使用。

在解决方案中,-secretCode.length - 1 基本上是一个常量,如果密码的长度为 6,则为 -7。这完全没有意义,可以用 0 替换,因为他们真的想访问第一个元素,这是应该出队的。

第二个参数是 pressed.length - secretCode.length,它计算到目前为止收集的密钥数量与密码总长度之间的差值。这是 <= 0 直到按下的队列超过密码的大小,此时它是 1,这意味着第一个元素出列,因为 splice 调用看起来像 splice(0,1)。当使用 splicesplice(0,-1) 等负数调用 splice(0,0) 时,它没有任何效果。

这是一个简化和带注释的版本:

const pressed = [];
var secretCode = 'wesbos';
window.addEventListener('keyup',e => {

  pressed.push(e.key);
  
  console.log(
    "after push,before splice",pressed + "",pressed.length - secretCode.length
  );

  pressed.splice(0,pressed.length - secretCode.length);
  
  console.log("after splice",pressed + "");
  console.log("______________");

  if (pressed.join('').toLowerCase() === secretCode) {
    console.log("SECRET COMMAND ACTION CODE TRIGGERED! COMMENCE KAMEHAMEHA");
  }
})
<p>type: "wesbos"</p>

我的观点是通常应该避免使用 splice,尤其是在处理负索引和添加元素时。它是线性的、聪明的、难以理解的,如果您必须从数组的中间弹出元素,通常会使用错误的数据结构。

我更喜欢你的方法,但我会这样写:

const pressed = [];
var secretCode = 'wesbos';
window.addEventListener('keyup',e => {
  pressed.push(e.key);
  
  while (pressed.length > secretCode.length) {
    pressed.shift();
  }

  if (pressed.join('').toLowerCase() === secretCode) {
    console.log("SECRET COMMAND ACTION CODE TRIGGERED! COMMENCE KAMEHAMEHA");
  }
})
<p>type: "wesbos"</p>

while 可能是 if,因为我们知道我们总是添加 1 个元素,但保留它 while 也没有什么坏处——关键是它强制出队,直到队列与目标词的大小相同。

JS 的一个恼人的事情是它没有一个好的内置队列结构,所以我们必须shift() 一个数组。这仍然是线性的,但至少它比 splice 更清楚地传达了实现队列的意图,Application.OpenForms 始终在索引 0 处运行并且始终删除不超过 1 个元素,尽管存在负索引混淆。