一个类里面还有一个类?

7
理想情况下,我想做以下这样的事情:
class Chess = {
  constructor() {
    this.board = ...;
    ...
  };

  class Square = {
    constructor(row, col) {
      this.row = row;
      this.col = col;
  };
};

我的主要动机是,通过将棋类和方块类分别定义,可以实现以下类似的功能:(此处指的是棋类)
this.empty(square)

可以缩短为
square.empty()

哪个更易读且更简洁。
不幸的是,我不能随便制作。
Square.empty()

由于结果取决于Chess类中的信息,因此这是一种方法。
square.empty(chess)

没有真正的改进。

我有一个正方形类的原因是类似于

square.up()

似乎比类似的东西要好得多。
[row, col + 1]

你有关于如何完成上述任务的建议吗?是否有一种方法可以在一个类内部编写另一个类,或者完全使用其他方法?
编辑:
根据likle和alex的建议,我做了以下操作:
我向Square类添加了一个context属性。
class Square = {
  constructor(context, row, col) {
    this.context = context;
    this.row = row;
    this.col = col;
  };
};

然后将一些来自Chess.prototype的方法重新定义到了Square.protoype中。例如:
// before
Chess.prototype.empty = function (square) {
  return this.piece(square) === 0;
};

// after
Square.prototype.empty = function () {
  return this.piece() === 0;
};

这意味着每次我创建一个Square对象时都需要添加上下文。例如:
new Square(3, 4); // before
new Square(this, 3, 4); // after
new Square(this.context, 3, 4); // sometimes like this

为了让代码更易读,我创建了以下方法:
Chess.prototype.createSquare = function (row, col) {
  return new Square(this, row, col);
};

一个Square对象有时可以通过以下方式创建:
this.createSquare(3, 4);

4
你可以将定义分开,让 Chess 只是实例化 Square 的实例。 - alex
3个回答

7
有人可能会认为JavaScript没有"嵌套"类,因为没有办法让一个类使用父类的作用域,也不会有任何意义,除非是访问父类属性(静态元素)。
在JavaScript中,类只是像其他任何对象一样的对象,因此您可以定义一个类并使用另一个类的属性引用它:
class Chess {
}

Chess.Square = class {
};

(是的,类的名称是可选的 - 上面,在Chess上的Square属性是指一个没有名称的类;这对于调试和内省可能不是很好,但仅仅是为了阐明一个观点)

有了以上内容,您可以做以下事情:

new Chess.Square();

通常情况下,你对一个类或者它的对象所做的所有其他操作--new只需一个作为构造函数的函数,而class声明实际上在程序运行时“退化”为构造函数--以下所有表达式的值都是true:

Chess instanceof Function;
typeof Chess == "function";
Chess.Square instanceof Function;
typeof Chess.Square == "function";
Chess.prototype.constructor == Chess;
Chess.Square.prototype.constructor == Chess.Square;

所以有嵌套的构造函数,现在我们已经确定了JavaScript类本质上就是它的构造函数。嗯,ECMAScript为每个类关联一些额外的元数据和一些差异,但这并不会影响到能够嵌套构造函数以访问外部作用域。
function Chess() {
    const chess = this;
    function Square() { /// Obviously, this function/constructor/class is only available to expressions and statements in the Chess function/constructor/class (and if explicitly "shared" with other code).
        Chess; /// Refers to the outer constructor/class -- you can do `new Chess()` etc
        this; /// Refers to this Square object being created
        chess; /// Refers to what `chess` declared outside this method, refers to at the time of creating this Square object
    }
}

上述代码使用了所谓的“闭包”,这需要你对JavaScript中的作用域有很好的掌握。在Java中,如上所述的Square类将被称为嵌套的实例类,而不是嵌套的静态类。虽然第一个示例在答案的顶部使用了class关键字,但它确实指定了后者的一种形式(即“静态”类)。
下面是另一种定义“实例”类Square的方法:
class Chess {
    constructor() {
        this.Square = class {
            constructor() {
                /// Be vigilant about `this` though -- in every function (except arrow functions) `this` is re-bound, meaning `this` in this constructor refers to an instance of the anonymous class, not to an instance of the `Chess` class; if you want to access the latter instance, you must have a reference to it saved for this constructor to access, e.g. with the `const chess = this;` statement in `Chess` constructor somewhere
            }
        }; /// Every `Chess` object gets a _distinct_ anonymous class referred to with a property named `Square`; is a class for every object expensive? is it needed? is it useful?
    }
}

JavaScript 的特点是它可以以不止一种方式实现面向对象编程,这可能比其他“面向对象语言”更多,而“嵌套类”对于不同的人和不同的程序意义不同。这被称为“给自己留下很多绳子来上吊”,这意味着你可以使用很多功能来优雅地实现你的模型,或者让你的代码难以阅读(尤其是对于那些不像你一样了解 JavaScript 的人)和调试。这是一个需要在全力投入“嵌套类”之前权衡的折衷方案。

4

目前,没有嵌套类。你可以创建两个单独的类,ChessChessSquare - 在ChessSquare的构造函数中传递对棋类的引用并将其存储为属性。这样,您就不必在ChessSquare的方法中传递它:

  class ChessSquare = {
    constructor(chess, row, col) {
      this.chess = chess;
      this.row = row;
      this.col = col;
    }

    empty() {
      // "this.chess" references the chess, and "this" references the square.
    }
  };

您可能希望在Chess类的内部创建所有ChessSquare实例。

0
以下代码段完美运行:
class Chess  {
  constructor() {
    this.board = {}
    for (var i=0; i<8; i++) {
          for (var j=0; j<8; j++) {
               this.board[i,j] = new Square(i,j)
               }
          }

  };

};

class Square {
    constructor(row, col) {
      this.row = row;
      this.col = col;
      this.color = "black"
      if ((row+col)%2 == 1) this.color = "white"
  };
};

两个独立的类在Chess(主)类中通过直接调用Square类相互关联,因此在实例化Chess时,棋盘方块也被定义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接