Javascript 为什么同一类的不同实例会相互覆盖?

Javascript 为什么同一类的不同实例会相互覆盖?,javascript,class,Javascript,Class,所以我的问题在于playAI和minimax算法 首先,我复制了一个实例myTicTacToe,我称之为tempgame,然后我在该副本上调用miniMaxAlgorithm。所有这些都是为了确保myTicTacToe保持不变 但问题是,myTicTacToe后来拥有与tempgame相同的价值观。 我不明白为什么 /**************************提克塔克托级**********************/ class TicTacToe { constructor(

所以我的问题在于playAI和minimax算法

首先,我复制了一个实例myTicTacToe,我称之为tempgame,然后我在该副本上调用miniMaxAlgorithm。所有这些都是为了确保myTicTacToe保持不变

但问题是,myTicTacToe后来拥有与tempgame相同的价值观。 我不明白为什么

/**************************提克塔克托级**********************/

class TicTacToe {




constructor(playField = [

    ['E', 'E', 'E'],
    ['E', 'E', 'E'],
    ['E', 'E', 'E']


], human = 'X', computer = 'O', gameStatus = "playing", currentPlayer = 'X') {

    this.playField = playField;


    this.human = human;
    this.computer = computer;
    this.gameStatus = gameStatus;
    this.bestMove;
    this.startingPlayer = human;


    this.currentPlayer = currentPlayer;

}



reset() {
    this.playField = [

        ['E', 'E', 'E'],
        ['E', 'E', 'E'],
        ['E', 'E', 'E']


    ];

    this.gameStatus = 'playing';
    this.currentPlayer = this.startingPlayer;
    $('#gamestate').text('');

}


checkGameState() {

    /******************** Win conditions******************************/
    if (this.currentPlayer === 'X' || this.currentPlayer === 'O') {

        if (this.winOrNot()) {


            $('#gamestate').text('Player ' + this.currentPlayer + ' won.');
            this.gameStatus = 'over';

        }



        //********************** Check if it is a draw***************/
        else {
            /*which results in a draw*/
            let arrayOfFreePlaces = this.freePositions();
            if (!(arrayOfFreePlaces.length > 0)) {

                $('#gamestate').text('It is a draw');
                this.gameStatus = 'over';
            }

        }

    }
}
/***********************/

winOrNot() {

    if ((this.playField[0][0] === this.currentPlayer && this.playField[0][1] === this.currentPlayer && this.playField[0][2] === this.currentPlayer) ||
        (this.playField[1][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[1][2] === this.currentPlayer) ||
        (this.playField[2][0] === this.currentPlayer && this.playField[2][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
        (this.playField[0][0] === this.currentPlayer && this.playField[1][0] === this.currentPlayer && this.playField[2][0] === this.currentPlayer) ||
        (this.playField[0][1] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][1] === this.currentPlayer) ||
        (this.playField[0][2] === this.currentPlayer && this.playField[1][2] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
        (this.playField[0][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
        (this.playField[0][2] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][0] === this.currentPlayer)

    ) {

        return true;

    } else {
        return false;
    }

}


freePositions() {

    let emptyPositions = [];

    for (let i = 0; i < this.playField.length; i++) {




        for (let j = 0; j < this.playField[i].length; j++) {

            if (this.playField[i][j] === 'E') {


                emptyPositions.push([i, j]);


            }



        }



    }



    return emptyPositions;



}
// rate gamestate

rateField() {
    // if computer wins +10
    if (this.winOrNot(this.computer)) {

        return 10;
    }
    // if human wins -10
    else if (this.winOrNot(this.human)) {

        return -10;
    }
    // if no one wins +0, aka drawm or not finished yet
    else {

        return 0;

    }


}




}

  //Prototypes of TicTacToe


   TicTacToe.prototype.placeSign = function(row, column) {
   // check if field is empty

if (this.playField[row][column] === 'E') {

    if (this.currentPlayer === "X") {


        this.playField[row][column] = 'X';

    } else if (this.currentPlayer === "O") {

        this.playField[row][column] = 'O';

    }
    this.checkGameState();
    this.currentPlayer === this.computer ? this.currentPlayer = this.human : this.currentPlayer = this.computer;


} else {


    console.log("Select an empty field!!");
}
    let myTicTacToe = new TicTacToe();
     let tempgame = new TicTacToe();
       let bestMove;
/*****************功能*************************/

class TicTacToe {




constructor(playField = [

    ['E', 'E', 'E'],
    ['E', 'E', 'E'],
    ['E', 'E', 'E']


], human = 'X', computer = 'O', gameStatus = "playing", currentPlayer = 'X') {

    this.playField = playField;


    this.human = human;
    this.computer = computer;
    this.gameStatus = gameStatus;
    this.bestMove;
    this.startingPlayer = human;


    this.currentPlayer = currentPlayer;

}



reset() {
    this.playField = [

        ['E', 'E', 'E'],
        ['E', 'E', 'E'],
        ['E', 'E', 'E']


    ];

    this.gameStatus = 'playing';
    this.currentPlayer = this.startingPlayer;
    $('#gamestate').text('');

}


checkGameState() {

    /******************** Win conditions******************************/
    if (this.currentPlayer === 'X' || this.currentPlayer === 'O') {

        if (this.winOrNot()) {


            $('#gamestate').text('Player ' + this.currentPlayer + ' won.');
            this.gameStatus = 'over';

        }



        //********************** Check if it is a draw***************/
        else {
            /*which results in a draw*/
            let arrayOfFreePlaces = this.freePositions();
            if (!(arrayOfFreePlaces.length > 0)) {

                $('#gamestate').text('It is a draw');
                this.gameStatus = 'over';
            }

        }

    }
}
/***********************/

winOrNot() {

    if ((this.playField[0][0] === this.currentPlayer && this.playField[0][1] === this.currentPlayer && this.playField[0][2] === this.currentPlayer) ||
        (this.playField[1][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[1][2] === this.currentPlayer) ||
        (this.playField[2][0] === this.currentPlayer && this.playField[2][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
        (this.playField[0][0] === this.currentPlayer && this.playField[1][0] === this.currentPlayer && this.playField[2][0] === this.currentPlayer) ||
        (this.playField[0][1] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][1] === this.currentPlayer) ||
        (this.playField[0][2] === this.currentPlayer && this.playField[1][2] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
        (this.playField[0][0] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][2] === this.currentPlayer) ||
        (this.playField[0][2] === this.currentPlayer && this.playField[1][1] === this.currentPlayer && this.playField[2][0] === this.currentPlayer)

    ) {

        return true;

    } else {
        return false;
    }

}


freePositions() {

    let emptyPositions = [];

    for (let i = 0; i < this.playField.length; i++) {




        for (let j = 0; j < this.playField[i].length; j++) {

            if (this.playField[i][j] === 'E') {


                emptyPositions.push([i, j]);


            }



        }



    }



    return emptyPositions;



}
// rate gamestate

rateField() {
    // if computer wins +10
    if (this.winOrNot(this.computer)) {

        return 10;
    }
    // if human wins -10
    else if (this.winOrNot(this.human)) {

        return -10;
    }
    // if no one wins +0, aka drawm or not finished yet
    else {

        return 0;

    }


}




}

  //Prototypes of TicTacToe


   TicTacToe.prototype.placeSign = function(row, column) {
   // check if field is empty

if (this.playField[row][column] === 'E') {

    if (this.currentPlayer === "X") {


        this.playField[row][column] = 'X';

    } else if (this.currentPlayer === "O") {

        this.playField[row][column] = 'O';

    }
    this.checkGameState();
    this.currentPlayer === this.computer ? this.currentPlayer = this.human : this.currentPlayer = this.computer;


} else {


    console.log("Select an empty field!!");
}
    let myTicTacToe = new TicTacToe();
     let tempgame = new TicTacToe();
       let bestMove;
//极小极大算法

  function miniMaxAlgorithm(TicTacToe1) {








/****************base case********************************/

// if the game is over , return rating
if (TicTacToe1.gameStatus === 'over') {
    return TicTacToe1.rateField();
}
/******************************************/
//contains the rating of each move
let scores = [];

// containing the equivalent moves
let moves = [];

//fill the scores array
/**************************recursive case*******************************/
// create on array with containing all possible moves of the current tictactoe instance
let freeFields = TicTacToe1.freePositions();
for (let i = 0; i < freeFields.length; i++) {
    //make a copy of the current tictactoe instance
    let possibleTicTacToe = new TicTacToe(TicTacToe1.playField, TicTacToe1.human, TicTacToe1.computer, TicTacToe1.gameStatus, TicTacToe1.currentPlayer);
    //play one of the possible moves
    possibleTicTacToe.placeSign(freeFields[i][0], freeFields[i][1]);
    // calling the function recursively until game is over 
    scores.push(miniMaxAlgorithm(possibleTicTacToe));
    // adding place sign parameters ass an array inside moves
    moves.push([freeFields[i][0], freeFields[i][1]]);
}


//  Min Max Calculation
if (TicTacToe1.currentPlayer === TicTacToe1.computer) {
    //      search for the largest score and save its index in maxScoreIndex
    let maxScoreIndex = 0;
    for (let j = 1; j < scores.length; j++) {
        if (scores[j] > scores[maxScoreIndex]) {
            maxScoreIndex = j;

        }

    }

    bestMove = moves[maxScoreIndex];
    return scores[maxScoreIndex];
}

// tests best possible opponent moves (human)
else {
    //        
    let minScoreIndex = 0;
    for (let j = 1; j < scores.length; j++) {
        if (scores[j] < scores[minScoreIndex]) {
            minScoreIndex = j;

        }



    }




    bestMove = moves[minScoreIndex];
    return scores[minScoreIndex];
}



/**********************************************************************/
//普拉伊

 function playAI() {

//AI miniMaxEnd

    tempgame = new TicTacToe (myTicTacToe.playField,myTicTacToe.human,myTicTacToe.computer,myTicTacToe.gameStatus,myTicTacToe.currentPlayer)

         console.dir(myTicTacToe);
        console.dir(tempgame);
         miniMaxAlgorithm(tempgame);
         console.dir(myTicTacToe);
         console.dir(tempgame);

    myTicTacToe.placeSign(bestMove[0],bestMove[1]);
  //AI miniMaxEnd




updateFields();

}

我知道是什么导致了这个问题

在javascript中,数组(或列表)是通过引用而不是值传递的

这意味着,如果将数组作为参数传递给函数,它将传递指向列表的指针,而不是列表的副本

TicTacToe类中,您有一个名为playField的变量,它是一个列表

创建TictaToe对象的克隆,如下所示:

tempgame = new TicTacToe (myTicTacToe.playField, ... )
在这里,您将传递对现有playField列表的引用,而不是克隆列表。i、 e.传递列表的地址而不是其内容

您的myTicTacToe和tempgame都将使用相同的游戏场数据副本。其中一个的变化将导致另一个的变化

为了创建数组的新副本,标准做法是使用javascript切片操作:

tempgame = new TicTacToe (myTicTacToe.playField.slice(0), ... )
此操作将从第一个元素(0)开始创建数组的新副本

有关使用切片操作克隆阵列的更多信息,请参见:


我知道是什么导致了这个问题

在javascript中,数组(或列表)是通过引用而不是值传递的

这意味着,如果将数组作为参数传递给函数,它将传递指向列表的指针,而不是列表的副本

TicTacToe类中,您有一个名为playField的变量,它是一个列表

创建TictaToe对象的克隆,如下所示:

tempgame = new TicTacToe (myTicTacToe.playField, ... )
在这里,您将传递对现有playField列表的引用,而不是克隆列表。i、 e.传递列表的地址而不是其内容

您的myTicTacToe和tempgame都将使用相同的游戏场数据副本。其中一个的变化将导致另一个的变化

为了创建数组的新副本,标准做法是使用javascript切片操作:

tempgame = new TicTacToe (myTicTacToe.playField.slice(0), ... )
此操作将从第一个元素(0)开始创建数组的新副本

有关使用切片操作克隆阵列的更多信息,请参见:


tempGame=JSON.parse(JSON.stringify(myTicTacToe))这将制作一份
myTicTacToe
的副本,并将其分配给
tempGame
仅适用于属性。不复制classIs的原型函数如果使用jQuery可以吗?我不明白。如何在类实例上使用jQuery这将制作一份
myTicTacToe
的副本,并将其分配给
tempGame
仅适用于属性。不复制classIs的原型函数如果使用jQuery可以吗?我不明白。如何在类实例上使用jQuery。尝试用javascript克隆对象可能很棘手。有人在这里遇到了一个类似的问题:好的,终于找到了解决方案,你非常接近了。我的代码的问题是我的类中有嵌套数组。这意味着即使我克隆了外部数组,内部数组仍然具有相同的引用。在这里读,酷!很高兴你明白了。我不认为嵌套数组需要嵌套切片,但我认为这是有意义的。用javascript克隆对象可能很棘手。有人在这里遇到了一个类似的问题:好的,终于找到了解决方案,你非常接近了。我的代码的问题是我的类中有嵌套数组。这意味着即使我克隆了外部数组,内部数组仍然具有相同的引用。在这里读,酷!很高兴你明白了。我不认为嵌套数组需要嵌套切片,但我认为这是有道理的。