对象的浅克隆? - Javascript

Shallow clone of an object? - Javascript

我的项目 SudokuBoard 有一个核心 objectSudokuBoard 唯一的字段是 2D arraySudokuBoard 的所有原型函数都涉及 2D array 并且需要它始终可见。

问题:

在即将出现的算法中,我需要 有一些方法来复制数独板。这是强制性功能。 每当我尝试复制时,它的二维数组只是对旧数组的引用。我不确定为什么。

我读到 cloneJavascript 中的噩梦,所以我有点担心。我是初学者,所以我最不想做的就是安装 Jquery 或使用一些外部库来解决这个问题。我提供了以下文件;他们应该 运行 没有错误。

SudokuBoard.js

/**
 * Constructs a SudokuBoard object.
 * Initializes the board with the numbers
 * provided to the constructor.
 * @param nums array, must be BOARD_SIZE^2 length.
 * @constructor
 */

function SudokuBoard(nums)
{
    // Private Fields:

    var BOARD_SIZE = 9;
    // The Sudoku board, represented as a 2D array.
    var gameboard = [];

    if (nums.length != BOARD_SIZE * BOARD_SIZE)
    {
        document.write("InvalidSizeError");
        throw "InvalidSizeError";
    }

    var counter = 0;
    for (var i = 0; i < BOARD_SIZE; i++)
    {
        var row = [];

        // Each row has a set amount of elements.
        while (row.length < BOARD_SIZE)
        {
            row.push(nums[counter]);
            counter++;
        }

        // Add the row to the board.
        gameboard.push(row);
    }

    SudokuBoard.prototype.getBoard = function()
    {
        return gameboard;
    }
}

/**
 * Gets all values within a row of the 2D array.
 * The Y coordinate works on the typical number
 * scale, meaning indexes start from 1, not 0.
 * Y corresponds to the vertical axis. The bottom
 * left of the board is at 1,1. The bottom right
 * of the board is at 9,1.
 * @param y coordinate of the row.
 * @returns {Array}
 */
SudokuBoard.prototype.getRow = function(y)
{
    return this.getBoard()[this.getBoard().length - y];
};

/**
 * Gets all values within a column of the 2D array.
 * The X coordinate works on the typical number
 * scale, meaning indexes start from 1, not 0.
 * X corresponds to the horizontal axis. The bottom
 * left of the board is at 1,1. The bottom right
 * of the board is at 9,1.
 * @param x coordinate of the column.
 * @returns {Array}
 */
SudokuBoard.prototype.getColumn = function(x)
{
    var column = [];

    for (var i = 1; i <= this.getBoard().length; i++)
    {
        column.push(this.getSlot(x, i));
    }

    return column;
};

/**
 * Algorithm which finds the correct quadrant of a given
 * coordinate and gets all the numbers which are contained
 * inside it. This operation relies on the fact that there
 * are three quadrants and once you make it so the first
 * index of quadrant one is considered as (3,3) you can
 * divide all X and Y values by 3 and yield their quadrant #.
 * @param x coordinate.
 * @param y coordinate.
 * @returns {Array}
 */
SudokuBoard.prototype.getQuadrant = function(x, y)
{
    // Determine what quadrant this coordinate is in.
    var horizQuad = Math.floor((x + 2) / 3); // 1 2 or 3
    var vertQuad = Math.floor((y + 2) / 3); // 1 2 or 3

    var quadrant = [];

    for (var i = 1; i <= 3; i++)
    {
        for (var h = 1; h <= 3; h++)
        {
            // Add the number to the array.
            quadrant.push(this.getSlot((horizQuad - 1) * 3 + i, (vertQuad - 1) * 3 + h));
        }
    }

    return quadrant;
};

/**
 * Gets a given slot on the board.
 * The X,Y coordinates work on the typical number
 * scale, meaning indexes start from 1, not 0.
 * X corresponds to the horizontal axis while Y
 * corresponds to the vertical axis. The bottom
 * left of the board is at 1,1. The bottom right
 * of the board is at 9,1.
 * @param x coordinate.
 * @param y coordinate.
 */
SudokuBoard.prototype.getSlot = function(x, y)
{
    return this.getBoard()[this.getBoard().length - y][x - 1];
};

/**
 * Sets a given slot on the board to a value.
 * The X,Y coordinates work on the typical number
 * scale, meaning indexes start from 1, not 0.
 * X corresponds to the horizontal axis while Y
 * corresponds to the vertical axis. The bottom
 * left of the board is at 1,1. The bottom right
 * of the board is at 9,1.
 * @param x coordinate.
 * @param y coordinate.
 * @param value to be placed.
 */
SudokuBoard.prototype.setSlot = function(x, y, value)
{
    this.getBoard()[this.getBoard().length - y][x - 1] = value;
};

SudokuBoard.prototype.clone = function()
{
    var numbers = [];

    for (var i = 0; i < this.getBoard().length; i++)
    {
        for (var h = 0; h < this.getBoard()[i].length; h++)
        {
            numbers.push(this.getBoard()[i][h]);
        }
    }

    return new SudokuBoard(numbers);
};

/**
 * ToString() method for SudokuBoard.
 * @returns {string}
 */
SudokuBoard.prototype.toString = function()
{
    const border = "+-----+-----+-----+";
    const nextline = "<br>";

    var temp = border + nextline;

    for (var i = 0; i < this.getBoard().length; i++)
    {
        temp += "|";

        for (var h = 0; h < this.getBoard()[i].length; h++)
        {
            // Every third character is proceeded by a |
            //\u00A0 for empty space.
            temp += ((this.getBoard()[i][h] == "0") ? "-" : this.getBoard()[i][h]) + ((h % 3 == 2) ? "|" : " ");
        }

        // Add a new line.
        temp += nextline;
    }

    // Return and add the bottom border.
    return temp + border;
};

Tester.js

var nums = [0, 0, 0, 0, 0, 0, 1, 4, 6,
            4, 0, 8, 7, 0, 0, 0, 0, 0,
            0, 6, 0, 0, 5, 0, 8, 9, 0,
            0, 0, 0, 1, 0, 0, 0, 0, 3,
            0, 8, 0, 0, 7, 4, 0, 0, 0,
            7, 0, 0, 0, 0, 0, 9, 0, 0,
            0, 0, 1, 8, 0, 9, 2, 0, 0,
            0, 0, 0, 5, 0, 0, 0, 0, 0,
            8, 0, 3, 0, 1, 7, 0, 0, 0];

var myBoard = new SudokuBoard(nums);
println("ORIGINAL:");
println(myBoard);

var clone = myBoard.clone();
println("CLONING:");
println(clone);

myBoard.setSlot(1, 1, 3);
println("CHANGED ORIGINAL:");
println(myBoard);

println("CLONE:");
println(clone);




/**
 * Used for debugging.
 * @param line
 */
function println(line)
{
    document.write(line + "<br>");
}

Runner.html

    <!DOCTYPE html>
<!--
  Project: SudokuSolver
  Name: Kevin
  Date: 2/12/2016
 -->

<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <font face="monospace"><font size="12">
    <!--Load from JavaScript file. -->
    <script type="text/javascript" src="SudokuBoard.js"></script>
    <script type="text/javascript" src="Tester.js"></script>
    </font></font>
</head>
<body>

</body>
</html>

基元按值传递,对象按"copy of a reference"传递。

这是我的导师给我的一个片段,用于克隆 JavaScript 个对象:

function cloneObj(obj) {
    var result = {};
    for(var key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (typeof obj[key] == 'object') {
                result[key] = cloneObj(obj[key]);
            } else {
                result[key] = obj[key];
            }
        }
    }
    return result;
};

--

或者试试这个 (from this post)?

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

尝试使用 David Flanagan 的扩展。

/*
 * Add a nonenumerable extend() method to Object.prototype.
 * This method extends the object on which it is called by copying properties
 * from the object passed as its argument.  All property attributes are
 * copied, not just the property value.  All own properties (even non-
 * enumerable ones) of the argument object are copied unless a property
 * with the same name already exists in the target object.
 */
Object.defineProperty(Object.prototype,
    "extend",                  // Define Object.prototype.extend
    {
        writable: true,
        enumerable: false,     // Make it nonenumerable
        configurable: true,
        value: function(o) {   // Its value is this function
            // Get all own props, even nonenumerable ones
            var names = Object.getOwnPropertyNames(o);
            // Loop through them
            for(var i = 0; i < names.length; i++) {
                // Skip props already in this object
                if (names[i] in this) continue;
                // Get property description from o
                var desc = Object.getOwnPropertyDescriptor(o,names[i]);
                // Use it to create property on this
                Object.defineProperty(this, names[i], desc);
            }
        }
    });


var mainObject = {name:'test'};
var clone = {};
clone.extend(mainObject);

我没有发现您的 .clone() 方法有任何问题。

问题在于您的构造函数,在于它如何设置 .getBoard() 方法。每次调用构造函数时,都会覆盖 prototype .getBoard() 方法,并且该方法引用由闭包捕获的局部变量,而不是实例变量。因此,为 SudokuBoard 的 any 实例调用 .getBoard() ,您将获得原型方法,该方法将从最新实例的闭包中引用局部变量,因此只有 return 最近创建的板.

更改此行:

SudokuBoard.prototype.getBoard = function()

创建实例方法而不是原型方法:

this.getBoard = function()

如果每个实例都有自己的 .getBoard() 它们都会引用自己的闭包。