如何禁止全局访问模块变量但允许其他模块访问?(JavaScript)
井字棋项目模块封装问题
我是JavaScript新手,正在开发一个井字棋项目。根据项目规范,我采用了模块模式:单一实例用模块实现,多实例用工厂创建。
目前我在gameBoard模块内创建了gameBoardArr数组存储棋局配置,但全局作用域能直接访问到这个数组。我需要禁止全局访问gameBoardArr,只允许displayController模块访问并修改它。
现有代码
gameBoard模块代码
let gameBoard = (function(){ 'use strict'; var gameBoardArr = ["","","","","","","","",""]; var gameBoardDisplay = () => { let gameboxes = [...document.querySelectorAll(".td")]; //console.log(gameboxes) gameboxes.forEach(Element=> { let index = Element.id; //console.log(gameBoard.gameBoardArr); if (gameBoard.gameBoardArr[index] == "X"){ Element.textContent = "⨉" } else if (gameBoard.gameBoardArr[index] == "O") { Element.textContent = "◯" } else { Element.textContent = gameBoard.gameBoardArr[index]; } }) } return {gameBoardDisplay, gameBoardArr}; })();
player工厂代码
let gamePlayer = (playerSymbol) => { let player = playerSymbol; return {player} }
displayController模块代码
let displayController = (function () { gameBoard.gameBoardDisplay(); let _count = 0; let _gameDiv = document.querySelector(".game-board"); let _welcomePanel = document.querySelector(".welcome"); let _selectionBtn1 = document.querySelector(".btn-1"); let _selectionBtn2 = document.querySelector(".btn-2"); var _resetBoard = () => { gameBoard.gameBoardArr = ["","","","","","","","",""]; gameBoard.gameBoardDisplay(); _count = 0; } var _congratulateWinner = (winnerName) => { playNextRoundBtn = document.createElement("button"); playNextRoundBtn.classList.add("play-next-round") playNextRoundBtn.textContent = "Play Next Round" //console.log("winnerbox"); winnerBox = document.querySelector(".congratulations"); winnerBox.textContent = ""; fullscreenbox = document.querySelector(".full-screenbox"); fullscreenbox.classList.remove("dont-show"); congratulationMsg = document.createElement("h1"); if (winnerName == "DRAW") { congratulationMsg.textContent = `Game Drawn`; } else { congratulationMsg.textContent = `congratulations ${winnerName}!` }; //console.log(referenceNode); winnerBox.appendChild(congratulationMsg); winnerBox.appendChild(playNextRoundBtn); setTimeout(function(){ playNextRoundBtn.addEventListener("click", () => { setTimeout(5000); fullscreenbox.classList.add("dont-show"); }); }, 50); }; var _checkWinner = (whatMark) => { //console.log(arr); let winningConfigs = [ [2, 5, 8], [0, 4, 8], [0, 1, 2], [0, 3, 6], [1, 4, 7], [2, 4, 6], [3, 4, 5], [6, 7, 8] ] for (i = 0; i < winningConfigs.length; i ++) { //console.log(winningConfigs[i][0]) //console.log(arr[winningConfigs[i][0]]); //console.log(whatMark); //console.log(arr[winningConfigs[i][0]] == whatMark); if (gameBoard.gameBoardArr[winningConfigs[i][0]] == whatMark && gameBoard.gameBoardArr[winningConfigs[i][1]] == whatMark && gameBoard.gameBoardArr[winningConfigs[i][2]] == whatMark) { return "win" } ; }; }; function _nextStep() { _gameDiv.classList.remove("dont-show"); _welcomePanel.classList.add("dont-show"); } function _HumanVHuman() { player1 = gamePlayer("X"); player2 = gamePlayer("O"); chanceFlag = true; cells = [...document.querySelectorAll(".td")]; cells.forEach(cell =>{ cell.addEventListener("click", ()=>{ if (chanceFlag && gameBoard.gameBoardArr[cell.id] == "") { gameBoard.gameBoardArr[cell.id] = "X"; _count = _count + 1 //console.log(gameBoard.gameBoardArr); chanceFlag = false; } else if (!chanceFlag && gameBoard.gameBoardArr[cell.id] == ""){ gameBoard.gameBoardArr[cell.id] = "O"; _count = _count + 1 chanceFlag = true; } gameBoard.gameBoardDisplay(); //console.log(chanceFlag); //console.log(gameBoard.gameBoardArr) setTimeout(function(){ let winStatus = _checkWinner(chanceFlag?"O":"X"); if (winStatus == "win" && chanceFlag == false) { _congratulateWinner("Player 1"); _resetBoard(); } else if (winStatus == "win" && chanceFlag == true) { _congratulateWinner("Player 2"); _resetBoard(); } else if (_count == 9) { console.log("first here"); _congratulateWinner("DRAW"); _resetBoard(); }; }, 50); //console.log(_count); }); }); }; function startGame() { _selectionBtn1.addEventListener("click", ()=>{ setTimeout(function(){ let gameMode = "HVH"; _nextStep(); _HumanVHuman(); //console.log(gameMode); }, 50); }); _selectionBtn2.addEventListener("click", ()=>{ let gameMode = "HVA"; //console.log(gameMode); }); }; return {startGame}; })();
全局作用域代码
displayController.startGame();
解决方案:封装棋盘操作接口
核心思路是不直接暴露gameBoardArr,而是提供专用的操作接口,让displayController通过接口完成棋盘读写、重置、胜负判断等操作,同时完全隔离全局作用域的访问权限。
修改后的gameBoard模块
let gameBoard = (function(){ 'use strict'; // 完全封装在模块内部,外部无法直接访问 var gameBoardArr = ["","","","","","","","",""]; var gameBoardDisplay = () => { let gameboxes = [...document.querySelectorAll(".td")]; gameboxes.forEach(Element=> { let index = Element.id; if (gameBoardArr[index] == "X"){ Element.textContent = "⨉" } else if (gameBoardArr[index] == "O") { Element.textContent = "◯" } else { Element.textContent = gameBoardArr[index]; } }) } // 获取指定位置的棋盘值 const getCell = (index) => { return gameBoardArr[index]; } // 设置指定位置的棋盘值,带合法性校验 const setCell = (index, value) => { if (index >= 0 && index < 9 && gameBoardArr[index] === "") { gameBoardArr[index] = value; gameBoardDisplay(); // 设置后自动更新显示 return true; // 设置成功返回true } return false; // 设置失败返回false } // 重置棋盘 const resetBoard = () => { gameBoardArr = ["","","","","","","","",""]; gameBoardDisplay(); } // 校验指定标记是否获胜 const checkWinner = (mark) => { const winningConfigs = [ [2, 5, 8], [0, 4, 8], [0, 1, 2], [0, 3, 6], [1, 4, 7], [2, 4, 6], [3, 4, 5], [6, 7, 8] ]; return winningConfigs.some(config => { return config.every(index => gameBoardArr[index] === mark); }); } // 只暴露操作接口,不暴露原始数组 return { gameBoardDisplay, getCell, setCell, resetBoard, checkWinner }; })();
修改后的displayController关键部分
// 重置棋盘改为调用接口 var _resetBoard = () => { gameBoard.resetBoard(); _count = 0; } // 移除原有的_checkWinner函数,改用gameBoard的接口 function _HumanVHuman() { player1 = gamePlayer("X"); player2 = gamePlayer("O"); chanceFlag = true; cells = [...document.querySelectorAll(".td")]; cells.forEach(cell =>{ cell.addEventListener("click", ()=>{ let success = false; // 通过getCell判断位置是否为空 if (chanceFlag && gameBoard.getCell(cell.id) === "") { success = gameBoard.setCell(cell.id, "X"); if (success) { _count++; chanceFlag = false; } } else if (!chanceFlag && gameBoard.getCell(cell.id) === ""){ success = gameBoard.setCell(cell.id, "O"); if (success) { _count++; chanceFlag = true; } } if (success) { setTimeout(function(){ let currentMark = chanceFlag ? "O" : "X"; // 调用gameBoard的checkWinner接口 let winStatus = gameBoard.checkWinner(currentMark); if (winStatus && chanceFlag === false) { _congratulateWinner("Player 1"); _resetBoard(); } else if (winStatus && chanceFlag === true) { _congratulateWinner("Player 2"); _resetBoard(); } else if (_count === 9) { _congratulateWinner("DRAW"); _resetBoard(); }; }, 50); } }); }); };
修改后,gameBoardArr完全被封装在模块内部,全局作用域无法直接访问,同时displayController可以通过暴露的接口正常完成所有游戏逻辑。
内容的提问来源于stack exchange,提问作者Shiva Kharbanda
相关产品推荐
相关产品推荐

