我正在尝试制作一个简单的井字游戏来学习和练习JavaScript OOP,并且遇到了一些困难。
在触发game.turn
函数时,我想引用game.handleClick
的值。我知道使用this
关键字引用了被调用对象的范围,在handleClick
中,this
引用了被点击的game-tile-x
。如何引用“ handleClick”功能范围之外的对象值?
任何帮助将不胜感激!
<div id="game-board">
<div id="game-tile-1" class="game-tile"></div>
<!-- divs 2-8 -->
<div id="game-tile-9" class="game-tile"></div>
</div>
function Game() {
this.tiles = document.getElementsByClassName('game-tile'),
this.turn = 0;
this.init = function() {
for (let i = 0; i < this.tiles.length; i++) {
// reset game tiles
this.tiles[i].innerHTML = '';
// setup event listeners
this.tiles[i].addEventListener('click', this.handleClick);
}
};
this.handleClick = function() {
let id = parseInt(this.id.replace('game-tile-', ''));
console.log(id); // returns 0, 1, 2, etc.
console.log(this.turn); // returns undefined
};
}
let game = new Game();
game.init();
答案 0 :(得分:1)
您可以使用curried函数,并使用游戏实例(this
)调用外部函数,然后在内部使用它:
this.tiles[i].addEventListener('click', this.handleClick(this));
//...
this.handleClick = function(game) {
return function() {
let id = parseInt(this.id.replace('game-tile-', ''));
console.log(id); // returns 0, 1, 2, etc.
console.log(game.turn);
};
};
答案 1 :(得分:1)
JavaScript的this
值是在调用时及其调用方式确定的。当您单击图块时,浏览器会通过调用handleClick
做出反应。 this
值通常是单击的元素。 this.turn
是undefined
,因为您的元素没有属性turn
。
您要做的是将this
的值存储在范围内的单独变量中。这样,您无需使用关键字this
就可以引用this
。
function Game() {
// The value of "this" in a constructor is the instance of the constructor.
// We store a reference of "this" in variable "foo" (could be anything really).
const foo = this
this.handleClick = function() {
// Use "instance" instead of "this".
console.log(foo.turn)
};
}
或者,您可以使用function.bind()
绑定this
的值(即创建一个相同的函数,其this
的值已经提前定义,而不是在调用时定义)。
function Game() {
this.init = function() {
for (let i = 0; i < this.tiles.length; i++) {
// Tell the browser that when it calls handleClick, the value
// of "this" is the one we provided in bind.
this.tiles[i].addEventListener('click', this.handleClick.bind(this))
}
}
this.handleClick = function() {
console.log(this.turn); // returns undefined
};
}
const game = new Game()
game.init()
请注意,此方法还取决于如何调用init()
,因为它还确定init
的{{1}}值是什么。如果我用不同的方式称呼它,如下所示:
this
const game = new Game()
const init = game.init()
init()
的{{1}}将是init
(正常模式)或this
(严格模式),这将导致绑定window
使用该值。
答案 2 :(得分:1)
这里是基本解决方案
function Game() {
this.tiles = document.getElementsByClassName('game-tile'),
this.turn = 0;
this.init = function() {
let tile;
for (let i = 0; i < this.tiles.length; i++) {
tile = this.tiles[i];
// reset game tiles
tile.innerHTML = '';
// setup event listeners
// don't mix this for the node & this for the game
// let's bind the context to the game, and the first param to the tile
tile.addEventListener('click', handleClick.bind(this, tile));
}
};
function handleClick(tile) {
let id = parseInt(tile.id.replace('game-tile-', ''));
console.log(id); // returns 0, 1, 2, etc.
console.log(this.turn); // returns undefined
};
}
let game = new Game();
game.init();
答案 3 :(得分:0)
有两种方法可以执行此操作,一种是将this
保存到以后将要使用的变量中(我们称其为闭包):
例如:
const self = this;
this.handleClick = function() {
const id = parseInt(this.id.replace('game-tile-', ''));
};
或调用bind,它将函数的this
对象绑定到当前作用域的this
:
this.handleClick = function() {
const id = parseInt(this.id.replace('game-tile-', ''));
};
this.handleClick = this.handleClick.bind(this);
PS:
如果可以使用ES6箭头功能:
this.handleClick = () => {
// this will point to the scope where the function is declared
}
如果可以使用ES6类:
class Game(){
constructor(){
this.tiles = document.getElementsByClassName('game-tile'),
this.turn = 0;
for (let i = 0; i < this.tiles.length; i++) {
// reset game tiles
this.tiles[i].innerHTML = '';
// setup event listeners
this.tiles[i].addEventListener('click', this.handleClick);
}
handleClick = () => {
let id = parseInt(this.id.replace('game-tile-', ''));
console.log(id); // returns 0, 1, 2, etc.
console.log(this.turn); // returns undefined
}
}
使用ES6语法更好,因为对于阅读器和机器而言都更易于理解。如果您担心浏览器的兼容性,请使用BabelJS
这样的编译器