从另一个实例初始化 JS 对象(运行时继承)

Initializing JS object from another instance (runtime inheritance)

我想实现这个:

var player = new Player (socket, x, y)
player.on ('event1', function () {});
player.emit ('event2', data);

其中 socket 是外部提供的 Socket 实例。

所以基本上我需要从 Socket 继承 Player,但是从 Socket 的实例初始化它:

var Player = function (socket, x, y)
{
    .....?
    this.x = x;
    this.y = y;
}

知道如何以最简单、最优雅的方式做到这一点吗?

根据你的问题,我假设你已经考虑过但拒绝使用组合:

var Player = function (socket, x, y)
{
    this.socket = socket;
    this.x = x;
    this.y = y;
};

用法:

var player = new Player(socket, x, y);
player.socket.on(/*...*/);
player.socket.emit(/*...*/);

FWIW,我可能会使用组合。

但如果你不想,你至少有两个选择:

  1. 真正继承,使用socket作为新对象的原型:

    在这种情况下,您可能不会使用 new:

    var Player = function (socket, x, y)
    {
        var obj = Object.create(socket);
        obj.x = x;
        obj.y = y;
        return obj;
    };
    

    用法:

    var player = Player (socket, x, y); // No `new` here, although it's harmless
    player.on ('event1', function () {});
    player.emit ('event2', function () {});
    

    (尽管如果您确实使用了 new,那将是无害的——由 new 创建的对象将被丢弃,因为 Player returns 是非-null 对象引用,因此会覆盖 new 的默认结果。)

    但是,如果您依赖继承 Player.prototype 的对象,这将不起作用。您可以 复制 Player.prototype 到实例的所有内容,方法是将以下代码添加到上面:

    var key;
    for (key in Player.prototype) {
        if (Player.prototype[key] !== Object.prototype[key]) {
            this[key] = Player.prototype[key];
        }
    }
    

    Player.prototype(及其原型,除了 Object.prototype 之外的任何属性复制到玩家对象上。不太理想,但功能齐全,这只是意味着您需要确保 Player.prototype 在创建第一个播放器之前包含所有内容。此外,instanceof 对玩家无效。

  2. 只需连接您想要的位,而无需实际使用 socket 作为原型,方法是将绑定函数添加到实际路由到 [=17= 的 Player 实例]:

    var Player = function (socket, x, y)
    {
        this.on = socket.on.bind(socket);
        this.emit = socket.emit.bind(socket);
        this.x = x;
        this.y = y;
    };
    

    然后使用 new.

    或者更冗长一点,但如果您需要,可以让您有机会进入中间:

    var Player = function (socket, x, y)
    {
        this.socket = socket;
        this.x = x;
        this.y = y;
    };
    Player.prototype.on = function() {
        return this.socket.on.apply(this.socket, arguments);
    };
    Player.prototype.emit = function() {
        return this.socket.emit.apply(this.socket, arguments);
    };
    

以上两个都使用了 ES5 中可以填充的特性。从 Socketonemit,我猜你正在使用 NodeJS,所以你正在使用 V8 并且你拥有这些功能。但是,如果不是,以及其他有类似问题的人:第一个解决方案使用 Object.create 的单参数版本,第二个使用 Function#bind。如果您需要支持较旧的 JavaScript 引擎,只需查找它们的 polyfill,它们都非常小。

术语 inheritance 在 JS 中与其他语言不同。具体来说,很多人一想到inheritance,就会想到JS中不存在的classical inheritance。相反,JS 使用 delegation 的概念来对对象进行原型设计,以模拟类似继承的行为。根据您的描述,您似乎希望 Player 的实例具有属于 Socket 的功能。在那种情况下,您所描述的更像是 Player 委托给 Socket.

的实例

下面是一些示例代码:

// assuming you have code that defines what a socket is
var Player = function (x, y) {
  var socket = new Socket();
  var p = Object.create( socket );
  p.x = x;
  p.y = y;

  return p;
};

Player 的实例现在可以将 属性 查找委托给 socket 实例。

So basically I need to inherit Player from Socket

不,你不知道。继承是一种 是一种 关系,而 Player 可能不是 Socket。另一方面,它可能会 使用 一个 Socket 实例 - 这种类型的关系可以通过 复合 关系建模。

因此您需要将 Socket 实例注入到您的 Player 构造函数中,以便在生成的 Player 实例中使用 player.

你在这里试图做的是使用继承来重用代码,多年来这已被证明是一种有缺陷的方法,因为它在父对象和子对象之间引入了紧密的隐式(通常是不需要的)耦合.根据您问题中的信息,组合是可行的方法。