"this" 值,在 Javascript 中使用工厂函数、模块、事件监听器和 IIFE 创建 Tic Tac Toe 游戏时
"this" value, when creating a Tic Tac Toe game using Factory Functions, Modules, Event Listeners, and IIFE's in Javascript
我正在开发一款 Tic Tac Toe 游戏,玩家轮流在棋盘上做标记。
在这个阶段,我试图让棋盘在每次单击方块时在玩家之间交替。因此,当玩家 1 单击棋盘时,玩家 1 的符号显示在单击的方格上,然后轮到玩家 2 select 一个方格。
这是我第一次使用立即调用的函数表达式 (IIFE) 和工厂函数,我很难让这些模块很好地协同工作。
过程和问题是这样的:
- 一个模块生成一个有 9 个 "square" 对象的板并添加一个
每个方形对象的 eventListener。 eventListener 在单击时触发一个函数。
- 那个eventListener函数应该做两件事:修改"square"对象的innerHTML,改变当前播放器
- eventListener函数使用了一个"this"变量,应该是"square"对象。 这就是问题所在。当控制台记录时,我发现 "this" 变量是 Window 对象,即使在 "square" 对象上调用了 eventListener。
- 当前结果是函数触发,但点击的 "square" 的 innerHTML 没有被修改,因为对象是 Window 而不是 "square" 对象。
我不明白为什么这不能正常工作,我很难理解如何在模块之间使用对象。
我查看了模块、IIFE 和 eventListeners,但似乎找不到一个很好的例子来结合所有这些并帮助我理解问题的根源。
//Gameboard array
var boardModule = (() => {
//Select div for board
let boardContainer = document.querySelector("#boardContainer")
let boardArray = ["x", "o", "x", "o", "x", "o", "x", "o", "x"];
//Module Player Selection
const gameFunctions = (() => {
// Onclick function for board
let currentPlayer;
// Selection module
const getPlayer = () => {
switch (currentPlayer) {
case undefined:
currentPlayer = 1;
case 1:
console.log(this);
this.innerHTML = "PLAYER 1 SELECTION";
return currentPlayer = 2;
case 2:
this.innerHTML = "PLAYER 2 SELECTION"
return currentPlayer = 1;
}
};
return {
getPlayer
};
})();
//Module: Built board with for each
const boardGenerator = () => {
boardArray.forEach(element => {
let square = document.createElement("div")
square.classList.add("squareStyle")
square.innerHTML = element
square.addEventListener("click", gameFunctions.getPlayer);
//Append to board
boardContainer.appendChild(square);
});
}
//Module: Build players
const Player = (name, mark) => {
getname = () => name;
getmark = () => mark;
const welcome = person => {
console.log(`Welcome to ${person.getname()}`)
}
return {
getname,
getmark,
welcome
}
}
player1 = Player("player1", "X");
player2 = Player("player2", "0");
//Module: Square Selector
return {
boardGenerator,
gameFunctions
};
})();
boardModule.boardGenerator();
html,
body {
height: 100%;
}
* {
font-family: Graphik Regular;
}
.container {
height: 100%;
padding: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
#boardContainer {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
position: absolute;
margin: 0 auto;
width: 300px;
height: auto;
}
.squareStyle {
display: flex;
height: 100px;
width: 100px;
background-color: red;
position: relative;
align-items: center;
justify-content: center;
}
<!DOCTYPE html>
<meta charset="UTF-8">
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</stylesheet>
<script type="text/javascript" src="http://livejs.com/live.js"></script>
</head>
<body>
<div class="container">
<div id="boardContainer">
</div>
</div>
<script src="script.js"></script>
</body>
</html>
发生这种情况是因为您正在使用箭头函数。箭头函数总是绑定到父作用域,在你的例子中是 window
对象。
看看这个来自 MDN Arrow Function Docs 的例子:
'use strict';
var obj = { // does not create a new scope
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this);
}
}
obj.b(); // prints undefined, Window {...} (or the global object)
obj.c(); // prints 10, Object {...}
我正在开发一款 Tic Tac Toe 游戏,玩家轮流在棋盘上做标记。
在这个阶段,我试图让棋盘在每次单击方块时在玩家之间交替。因此,当玩家 1 单击棋盘时,玩家 1 的符号显示在单击的方格上,然后轮到玩家 2 select 一个方格。
这是我第一次使用立即调用的函数表达式 (IIFE) 和工厂函数,我很难让这些模块很好地协同工作。
过程和问题是这样的:
- 一个模块生成一个有 9 个 "square" 对象的板并添加一个 每个方形对象的 eventListener。 eventListener 在单击时触发一个函数。
- 那个eventListener函数应该做两件事:修改"square"对象的innerHTML,改变当前播放器
- eventListener函数使用了一个"this"变量,应该是"square"对象。 这就是问题所在。当控制台记录时,我发现 "this" 变量是 Window 对象,即使在 "square" 对象上调用了 eventListener。
- 当前结果是函数触发,但点击的 "square" 的 innerHTML 没有被修改,因为对象是 Window 而不是 "square" 对象。
我不明白为什么这不能正常工作,我很难理解如何在模块之间使用对象。
我查看了模块、IIFE 和 eventListeners,但似乎找不到一个很好的例子来结合所有这些并帮助我理解问题的根源。
//Gameboard array
var boardModule = (() => {
//Select div for board
let boardContainer = document.querySelector("#boardContainer")
let boardArray = ["x", "o", "x", "o", "x", "o", "x", "o", "x"];
//Module Player Selection
const gameFunctions = (() => {
// Onclick function for board
let currentPlayer;
// Selection module
const getPlayer = () => {
switch (currentPlayer) {
case undefined:
currentPlayer = 1;
case 1:
console.log(this);
this.innerHTML = "PLAYER 1 SELECTION";
return currentPlayer = 2;
case 2:
this.innerHTML = "PLAYER 2 SELECTION"
return currentPlayer = 1;
}
};
return {
getPlayer
};
})();
//Module: Built board with for each
const boardGenerator = () => {
boardArray.forEach(element => {
let square = document.createElement("div")
square.classList.add("squareStyle")
square.innerHTML = element
square.addEventListener("click", gameFunctions.getPlayer);
//Append to board
boardContainer.appendChild(square);
});
}
//Module: Build players
const Player = (name, mark) => {
getname = () => name;
getmark = () => mark;
const welcome = person => {
console.log(`Welcome to ${person.getname()}`)
}
return {
getname,
getmark,
welcome
}
}
player1 = Player("player1", "X");
player2 = Player("player2", "0");
//Module: Square Selector
return {
boardGenerator,
gameFunctions
};
})();
boardModule.boardGenerator();
html,
body {
height: 100%;
}
* {
font-family: Graphik Regular;
}
.container {
height: 100%;
padding: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
#boardContainer {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
position: absolute;
margin: 0 auto;
width: 300px;
height: auto;
}
.squareStyle {
display: flex;
height: 100px;
width: 100px;
background-color: red;
position: relative;
align-items: center;
justify-content: center;
}
<!DOCTYPE html>
<meta charset="UTF-8">
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</stylesheet>
<script type="text/javascript" src="http://livejs.com/live.js"></script>
</head>
<body>
<div class="container">
<div id="boardContainer">
</div>
</div>
<script src="script.js"></script>
</body>
</html>
发生这种情况是因为您正在使用箭头函数。箭头函数总是绑定到父作用域,在你的例子中是 window
对象。
看看这个来自 MDN Arrow Function Docs 的例子:
'use strict';
var obj = { // does not create a new scope
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this);
}
}
obj.b(); // prints undefined, Window {...} (or the global object)
obj.c(); // prints 10, Object {...}