【问题标题】:What type of code is `gameplay` in my code and how does it function?我的代码中的“游戏玩法”是什么类型的代码,它是如何工作的?
【发布时间】:2020-06-29 21:54:21
【问题描述】:

我知道这是一个奇怪的请求,但我不完全理解为什么我的代码从句法的角度来看是有效的。需要明确的是,它确实按预期工作,完全是我自己的代码,但我对我为达到我所在的位置所做的事情有点迷茫。每个带有 `gameplay' 对象的单独函数(或函数,我不知道它叫什么)都可以忽略。

const Player = function(name, symbol, score) {
  return {name, symbol, score};
}
let playerOne = new Player(document.getElementById('oneName').value, 'x', 0);
let playerTwo = new Player(document.getElementById('twoName').value, 'o', 0);
 const updateNames = function() {
  playerOne.name = document.getElementById('oneName').value;
  playerTwo.name = document.getElementById('twoName').value;
}
const gameplay = (() => {
  let turn = 'x';
  const board = {
    1: '',
    2: '',
    3: '',
    4: '',
    5: '',
    6: '',
    7: '',
    8: '',
    9: ''
  }
  const checkWinner = function() {
    let winCombinations = [
      [1, 2, 3],
      [4, 5, 6],
      [7, 8, 9],
      [1, 4, 7],
      [2, 5, 8],
      [3, 6, 9],
      [1, 5, 9],
     [7, 5, 3],
    ];
    winCombinations.forEach(function(array) {
      let x = 0;
      Object.keys(board).forEach(function(key) {
        if (board[key]) {
          x += 1;
        }
      })
      if (board[array[0]] === 'x' && board[array[1]] === 'x' && board[array[2]] === 'x') {
        displayWinner(playerOne.name ? playerOne.name : 'Player One');
        playerOne.score += 1;
        document.getElementById('oneScore').innerHTML = playerOne.score;
      } else if (board[array[0]] === 'o' && board[array[1]] === 'o' && board[array[2]] === 'o') {
        displayWinner(playerTwo.name ? playerTwo.name : 'Player Two');
        playerTwo.score += 1;
        document.getElementById('twoScore').innerHTML = playerTwo.score;
      } else if (x === 9) {
        displayWinner(false);
      };
    })
  }
  const displayWinner = function(winner) {
    const x = document.getElementById('winMessage');
    x.innerHTML = winner ? `${winner} is the winner!` : 'TIE!';
    x.style.display = 'flex';
    x.addEventListener('click', refresh);
  }
  const refresh = function() {
    updateNames();
    const x = document.getElementById('htmlBoard');
    while (x.firstChild) {
      x.removeChild(x.lastChild)
    }
   for (i = 1; i < 10; i++) {
      board[i] = '';
    };
    document.getElementById('winMessage').style.display = 'none';
    turn = 'x';
    render();
  }
  const restart = function() {
    playerOne.score = 0;
    playerTwo.score = 0;
    document.getElementById('oneScore').innerHTML = playerOne.score;
    document.getElementById('twoScore').innerHTML = playerTwo.score;
    const x = document.getElementById('htmlBoard');
    refresh();
    turn = 'x';
  }
  const commitPlay = function() {
    if (!(board[event.target.id])) {
      if (turn === 'x') {
        event.target.className = 'x';
        event.target.style.cursor = 'not-allowed';
        board[event.target.id] = 'x';
        turn = 'o';
      } else {
        event.target.className = 'o';
        event.target.style.cursor = 'not-allowed';
        board[event.target.id] = 'o';
        turn = 'x';
      }
      checkWinner();
    }
  }
  const render = function() {
    for (i = 1; i <= 9; i++) {
      let tile = document.createElement('div');
      tile.id = i;
      document.getElementById('htmlBoard').appendChild(tile);
      tile.addEventListener('click', commitPlay);
    }
  }
  return {board, render, restart, refresh};
})(); //<----

document.getElementById('clearBoard').addEventListener('click', gameplay.refresh);
document.getElementById('newGame').addEventListener('click', gameplay.restart);
window.onload = function () {
  gameplay.render();
};

我的主要问题是关于gameplay 对象/函数。它是什么,它如何影响我的代码的工作方式?此外,我在它的最后一行添加了一个箭头,因为我不明白为什么我需要在同一行上的左括号和右括号,里面什么都没有,但没有它们就行不通。

由于这是一个如此奇怪的问题并且措辞可怕,我将密切关注这个问题以澄清任何事情。提前致谢。

【问题讨论】:

  • 这是一个 IIFE (developer.mozilla.org/en-US/docs/Glossary/IIFE),只是带有箭头函数而不是“常规”function
  • 打开和关闭括号会立即调用您定义的函数('invoke')。该函数内部的所有内容都不再被该函数之外的任何人访问,除了它返回的内容。

标签: javascript object constructor


【解决方案1】:

让我们看看这些片段(带有一些抽象)。

() => {
    console.log("foo");
}

这是什么?它只是一个函数,但它是完全匿名的,因为你没有命名它,也不能通过名称调用它。它本质上等同于:

var someFunc = function() {
    console.log("foo");
}

除了在后一个示例中,您现在有一个句柄来通过 someFunc 变量调用匿名函数。

如果你愿意,你可以对箭头语法定义的匿名函数做同样的事情。但这不是您在示例代码中所做的。相反,你有这样的东西:

const gameplay = (() => {
    // stuff happens in here
})();

您指出您对它的作用和工作原理感到困惑。

本质上,你有一个匿名箭头函数,包裹在一对括号中,最后是()。好吧,让我们换一种方式来看,它会做同样的事情......

const temporaryFunctionHandle = () => {
    // stuff happens in here
};
const gameplay = temporaryFunctionHandle();

这本质上是等价的,只是你定义了一个变量来存储匿名函数。将匿名函数包装在括号中然后在末尾放置另一对括号(); 所做的就是调用该函数而不将其存储到变量中。它前面的const gameplay = 只是将该函数调用中的return 分配给gameplay 变量,这就是为什么当匿名函数返回一个形状为{board, render, restart, refresh} 的对象时,您可以通过以下方式访问这些属性gameplay.boardgameplay.render 等等。

这是另一种可以编写并执行基本相同任务的方式:

function buildGameplayObject() {
    // do stuff
    return { board, render, restart, refresh};
}

const gameplay = buildGameplayObject();

如您所见,如果您没有在const gameplay = ... 行的末尾包含();,您将不会执行buildGameplayObject 函数并将其返回值分配给gameplay 变量,但是您只需将对 buildGameplayObject 函数的引用分配给 gameplay 变量。然后您需要调用gameplay()(或buildGameplayObject())并将返回值分配给另一个变量以访问对象的返回属性。

这就是const gameplay = (() =&gt; {}})(); 末尾的(); 所做的——它显式调用已创建的匿名函数并将返回的对象分配给gameplay 变量。

但是为什么?

查看这种语法时的一个常见问题是……为什么要这样做?好吧,答案很简单,在匿名函数内部你创建了很多变量,你声明了一些你不需要或不想直接访问的函数,你只想访问那些结果属性(在本例中为函数)。它类似于您对 Player 函数所做的事情,除了您只需要匿名函数的结果一次,因此一旦您构建了 gameplay 对象,您就可以安全地放弃对它的访问。

希望这有助于澄清您对代码中正在发生的事情的理解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多