我应该在 JavaScript 中嵌套构造函数和原型吗?

Should I nest constructors and prototypes in JavaScript?

我目前正在处理的一个项目需要一种对象类型 Chain,它本身会生成一种仅由它使用的对象类型 Link。 在以前的项目中,我将 Link 的对象构造函数和原型嵌套在 Chain 的构造函数中,但是,这次我开始怀疑这是否是最好的方法,因为我实际上会这样做似乎每次都用自己对 Link 的定义来设置每个 Chain

为清楚起见,这是我的原始代码:

var Chain = function() {
    var self = this;

    var Link = function(key) {
        this.prop = key;
    };
    Link.prototype.attach = function(args) {
        for (let value of args) {
            this[value.prop] = value.value;
        }
    };

    self.links = [];

}
Chain.prototype.add = function(key) {
    this.links.push(new Link(key));
}

然后我开始想知道我是否应该像 Chain 那样将 Link 构造函数和原型存储在野外(为了记录,它不是在野外,它实际上包含在一个对象中)或者我是否应该使用 Chain 的原型来定义 Link 并将其保留在那里。

我没能找到很多关于这种情况下最佳实践的信息,虽然我强烈怀疑我的第一种做事方式不是 best/right 方式;我不确定我的替代方案是否也是。

所以我问,什么是最好的 practice/most efficient/sensible 方法?

In previous projects, I had nested the object constructor and prototype of Link inside the constructor for Chain, however, I began to wonder this time if this was the best way...

I have then begun wondering if I should just store the Link constructor and prototype out in the wild like I have with Chain

还有第三种选择:LinkChain 私有的:

var Chain = (function() {
    function Link() {
        // ...
    }
    // Link.prototype stuff here

    function Chain() {
        // ...
    }
    // Chain.prototype stuff here

    return Chain;
})();

现在,Chain 是 public,Link 是私有的,但是只有一个 Link 而不是为每个 Chain 创建一个新的。

这同样适用于 ES2015+ class 语法:

const Chain = (function() {
    class Link {
        // ...
    }

    class Chain {
        // ...
    }

    return Chain;
})();

I began to wonder this time if this the best way to do this as I would actually be appearing to set up each Chain with its' own definition of Link every time.

是的,这正是不这样做的原因。当你创建一千个链时,你不想创建数千个 Link classes。始终将 class 彼此相邻放置,永远不要将一个放在另一个的构造函数中。

有时,特定于实例的辅助函数可能会有所帮助(设置属于该特定链的 link),但可以将其作为工厂方法来实现,并且仍然保留所有 link 来自所有链的共享相同的(基数)class.

Should I just store the Link constructor and prototype out in the wild like I have with Chain?

是的,就范围而言,这很好。除非 "the wild" 是全局范围,否则这没有错。如果您想进行一些封装,可以使用 ES6 模块仅导出该范围的 "public" 部分,或者使用 IIFE(如@T.J.Crowder 的回答)。

另一种将 class 保持在一起的常用方法是将 class 用作命名空间对象:

var Chain = function() {
    this.links = [];
};
… // Chain.prototype

Chain.Link = function(key) {
    this.prop = key;
};
… // Chain.Link.prototype

相同.