嵌套的ES6类?

93

似乎可以将一个类嵌套在构造函数中,然后可以在类的任何地方实例化它,这是否是官方支持的?

[编辑] 例如:

class C {

    constructor() {
        class D {
            constructor() { }
        }
    }

    method() {
        var a = new D();  // works fine
    }

}

//var a = new D();  // fails in outer scope

这是由traceur生成的JS代码:https://google.github.io/traceur-compiler/demo/repl.html

$traceurRuntime.ModuleStore.getAnonymousModule(function() {
  "use strict";
  var C = function C() {
    var D = function D() {};
    ($traceurRuntime.createClass)(D, {}, {});
  };
  ($traceurRuntime.createClass)(C, {method: function() {
      var a = new D();
    }}, {});
  return {};
});
//# sourceURL=traceured.js

你能提供一个具体的例子吗? - Felix Kling
或者你是怎么发现这个问题的?目前还没有浏览器实现类。 - Felix Kling
我确定我在某个地方读到过这个,并且它确实留在了我的脑海中 - 使用traceur进行了黑客攻击(参考上面的链接)。 - user5321531
1
那很可能是traceur中的一个bug。 - Felix Kling
不,这在 ES6 类中并非官方的。但是,如果您将 var a = new D() 替换为 var a = new this.D(),那么就没有问题了。 - Redu
4个回答

111

不,ES6没有嵌套类作用域,并且如果你的意思是这样的话,在类语法中也不存在私有成员这样的东西。

当然,您可以将第二个类作为另一个类的静态属性放置在其中,就像这样:

class A {
    …
}
A.B = class {
    …
};

或者您可以使用额外的作用域:

var C;
{
    class D {
        constructor() { }
    }
    C = class C {
        constructor() { }
        method() {
            var a = new D();  // works fine
        }
    }
}

由于traceur使用了被提升的var作为类声明,而不是块级作用域,似乎存在一个bug。


有了class field syntax,也可以编写单个表达式或声明。

class A {
    …
    static B = class {
         …
    }
};

6
否则类将成为匿名类,应该是 A.B = class B { .. - levi
1
@levi:匿名类没有任何问题 :-) 当然,如果您想要添加它,可以这样做。 - Bergi
1
@Dakusan 不,那不是JS的一种风格。那是TypeScript(你可能已经设置为编译成ES6)。 - Bergi
1
意译:虽然这个线程有点过时,但它仍然提供了有用的指导。关于在另一个类上定义“第二类为静态属性”,通过 A.B = class {...} 定义 B 似乎不允许在类 A 中创建类 B 的实例(至少在 Chrome v76 上是如此)。例如,在类 A 函数内部,let n = new this.B() 会产生 Uncaught TypeError: this.B is not a constructor 的错误。通过 A.prototype.B = class {...} 定义 B 可以解决这个问题... - Trentium
2
@JonTrent 你需要引用 A 本身,而不是它的一个实例。你可以使用 A.Bthis.constructor.B。我建议避免将类放在原型上。 - Bergi
显示剩余11条评论

9

你是一位有用的助手,可以翻译文本。

class A {
    constructor () {
        this.B = class {
            echo () {
                console.log('I am B class');
            }
        }
    }
    echo () {
        this.b = new this.B;
        this.b.echo();
    }
}

var a = new A;

a.echo();


在你的回答中添加更多信息,不仅仅是代码块。 - live2
相对于Bergi的答案,这个选项的缺点是对于类A的每个实例,都会有一个包含类B定义的属性。 - Trentium
这是唯一正确的答案。this.b = new this.B; 部分很重要。 - Redu
@Ammatwain 你知道我们使用这个需要注意的内存方面的问题吗? - It's K

8
您可以使用一个 getter 方法:
class Huffman {
  constructor() { /* ... */ }
  static get Node() {
    return class Node {
      constructor() {  
        var API = this;
        API.symbol = 0; API.weight = 0;
        return API;    
      }
    };
  }
  get Node() {
    return Huffman.Node;
  }
  encode() { /* ... */ }
  decode() { /* ... */ }
  /* ... */
}

// usage
huffman = new Huffman;
new huffman.Node;
new Huffman.Node;

在最新的Chrome Dev 44.0.2376.0上,苹果10.10.2版本控制台输出:

  • new huffman.Node
  • Node {symbol: 0, weight: 0}
  • new Huffman.Node
  • Node {symbol: 0, weight: 0}

另外,getter是ES6中让你可以做很多有趣事情的神奇元素。

请注意:上述结构破坏了instanceof对于Node的支持(为什么?因为每次调用get时都会定义一个全新的类)。为了不破坏instanceof,请在单个getter的作用域之外定义Node,可以在构造函数中(禁用Huffman.Node类属性,并导致instanceof在单个Huffman实例的命名空间内工作,在该命名空间之外则失效),或者在Huffman的兄弟或祖先作用域中定义Node(允许instanceof在下面所有作用域中工作,其中Node被定义)。


1
如果打破instanceof/原型链,性能也会受到影响。你真的应该只使用huffman.Node = class { constructor(){ this.symbol=0; this.weight=0; } }; - Bergi
现在我倾向于每个模块/文件编写一个类,但是如果一个类用于填充另一个类的属性(即当其所属的类的实例被删除时也会被删除),并且没有被其他类使用,则我会将该类放在同一个模块中 - 这实际上就像嵌套类一样(但不完全相同,在这种情况下无法将类嵌套在嵌套类中,或许可以考虑嵌套作用域)。 - user5321531
等等,你代码中的那些类(以及OP想要的那个类)不是实例的属性,而是与整个类本身绑定的。 - Bergi
非静态的get是实例的属性,而静态的get是类的属性。另一个好的解决方案是将相关的类放入一个模块中,这样我们就可以拥有一个Huffman模块,其中包含编码器、节点和解码器类。它们都是模块属性,并且在同一作用域内。 - user2530580

4

当你在父类的构造函数中创建一个嵌套的子类时,这意味着每个父类实例都有自己的子类。通常这不是你想要的。相反,你想要一个子类,它在所有父类实例之间共享。这意味着嵌套类必须是静态的。以下是一个示例:

class Parent
{
  static Child = class Child {
    constructor (name) { console.log (`Child: ${name}`); }
  }
  
  constructor (...names) {
    console.log ('Parent');
    this.children = names.map (name => new Parent.Child (name));
  }
}

var p = new Parent ('Alice', 'Bob');

console.log (`same type? ${p.children[0].constructor === p.children[1].constructor}`);


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