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

Javascript 从另一个实例初始化JS对象(运行时继承),javascript,inheritance,prototype,Javascript,Inheritance,Prototype,我想做到这一点: var player = new Player (socket, x, y) player.on ('event1', function () {}); player.emit ('event2', data); 其中,套接字是外部提供的套接字的实例 所以基本上我需要从Socket继承Player,但从Socket的实例初始化它: var Player = function (socket, x, y) { .....? this.x = x; thi

我想做到这一点:

var player = new Player (socket, x, y)
player.on ('event1', function () {});
player.emit ('event2', data);
其中,套接字是外部提供的套接字的实例

所以基本上我需要从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(/*...*/);
var player = Player (socket, x, y); // No `new` here, although it's harmless
player.on ('event1', function () {});
player.emit ('event2', function () {});
FWIW,我可能会使用构图

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

真正继承,使用套接字作为新对象的原型:

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

用法:

var player = new Player(socket, x, y);
player.socket.on(/*...*/);
player.socket.emit(/*...*/);
var player = Player (socket, x, y); // No `new` here, although it's harmless
player.on ('event1', function () {});
player.emit ('event2', function () {});
尽管如果您确实使用了new,它将是无害的——new创建的对象将被丢弃,因为Player返回一个非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已将这些属性复制到Player对象上。虽然不太理想,但很实用,这只意味着在创建第一个播放器之前,您需要确保Player.prototype上有所有内容。此外,instanceof对玩家不起作用

只需将所需的位连接起来,而无需实际使用socket作为原型,方法是将绑定函数添加到实际路由到socket的播放器实例:

使用方法和你的问题一样,使用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中可以多填充的功能。从Socket、on和emit,我猜您使用的是NodeJS,所以您使用的是V8,并且您拥有这些特性。如果不是,那么对于其他有类似问题的人来说:第一种解决方案使用Object.create的单参数版本,第二种使用Functionbind。如果您需要支持较旧的JavaScript引擎,只需查找它们的多边形填充,两者都非常小。

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

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(/*...*/);
var player = Player (socket, x, y); // No `new` here, although it's harmless
player.on ('event1', function () {});
player.emit ('event2', function () {});
FWIW,我可能会使用构图

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

真正继承,使用套接字作为新对象的原型:

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

用法:

var player = new Player(socket, x, y);
player.socket.on(/*...*/);
player.socket.emit(/*...*/);
var player = Player (socket, x, y); // No `new` here, although it's harmless
player.on ('event1', function () {});
player.emit ('event2', function () {});
尽管如果您确实使用了new,它将是无害的——new创建的对象将被丢弃,因为Player返回一个非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已将这些属性复制到Player对象上。虽然不太理想,但很实用,这只意味着在创建第一个播放器之前,您需要确保Player.prototype上有所有内容。此外,instanceof对玩家不起作用

只需将所需的位连接起来,而无需实际使用socket作为原型,方法是将绑定函数添加到实际路由到socket的播放器实例:

使用方法和你的问题一样,使用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中可以多填充的功能。从Socket、on和emit,我猜您使用的是NodeJS,所以您使用的是V8,并且您拥有这些特性。如果不是,那么对于其他有类似问题的人来说:第一种解决方案使用Object.create的单参数版本,第二种使用Functionbind。如果您需要支持较旧的JavaScript引擎,只需查找polyfill即可,两者都非常小。

与其他语言相比,JS中的术语继承是不同的。具体地说,当许多人想到继承时,他们想到的是经典继承,这在JS中是不存在的。相反,JS使用委托的概念来原型化对象,以模拟类似继承的行为。根据您所描述的,您似乎希望Player的实例具有属于套接字的功能。在这种情况下,您所描述的更像是玩家委托给套接字的实例

下面是一些示例代码:

// 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实例现在可以将属性查找委托给套接字实例。

JS中的术语继承与其他语言不同。具体地说,当许多人想到继承时,他们想到的是经典继承,这在JS中是不存在的。相反,JS使用委托的概念来实现原型 对象以模拟类似继承的行为。根据您所描述的,您似乎希望Player的实例具有属于套接字的功能。在这种情况下,您所描述的更像是玩家委托给套接字的实例

下面是一些示例代码:

// 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实例现在可以将属性查找委托给套接字实例

所以基本上我需要从套接字继承播放器

不,你没有。继承是一种is-a关系,玩家可能不是套接字。另一方面,它可能使用套接字实例,这种类型的关系可以由复合关系建模

因此,您需要将套接字实例注入到播放器构造函数中,以便在生成的播放器实例播放器中使用

您在这里尝试的是使用继承进行代码重用,多年来已经证明这是一种有缺陷的方法,因为它在父对象和子对象之间引入了紧密的隐式耦合,而且通常是不必要的耦合。根据你问题中的信息,写作是一条出路

所以基本上我需要从套接字继承播放器

不,你没有。继承是一种is-a关系,玩家可能不是套接字。另一方面,它可能使用套接字实例,这种类型的关系可以由复合关系建模

因此,您需要将套接字实例注入到播放器构造函数中,以便在生成的播放器实例播放器中使用


您在这里尝试的是使用继承进行代码重用,多年来已经证明这是一种有缺陷的方法,因为它在父对象和子对象之间引入了紧密的隐式耦合,而且通常是不必要的耦合。根据你问题中的信息,写作是一条必由之路。

而且。。3.组合:this.socket=socket;然后是player.socket.on'event1',函数{}。4.Mixin/extend.@dfsq:确实如此。我以为OP已经考虑并拒绝了这篇文章,但我应该把它挂起来——这将是我的第一选择。我会的。我可能不会在这里使用mixin,因为Socket是由OP控制之外的代码定义的,所以有太多的机会来践踏这些东西。谢谢您的详细回答。第一个解决方案似乎可行,但当通过Player.prototype.foo=函数{…}添加自定义功能并调用Player.foo时,会导致Player.foo未定义!第二个解决方案完美无瑕,所以我接受它。谢谢我拒绝了播放器。插座。在。。。构图,因为它看起来没有我的品味优雅。我想让我的玩家看起来像是直接发射和接收事件。@VincentPride:是的,在你发表评论前几分钟,我刚刚在答案中添加了一条关于Player.prototype的注释,你可能没有看到它。:-很高兴第二种选择有效!:-而且。。3.组合:this.socket=socket;然后是player.socket.on'event1',函数{}。4.Mixin/extend.@dfsq:确实如此。我以为OP已经考虑并拒绝了这篇文章,但我应该把它挂起来——这将是我的第一选择。我会的。我可能不会在这里使用mixin,因为Socket是由OP控制之外的代码定义的,所以有太多的机会来践踏这些东西。谢谢您的详细回答。第一个解决方案似乎可行,但当通过Player.prototype.foo=函数{…}添加自定义功能并调用Player.foo时,会导致Player.foo未定义!第二个解决方案完美无瑕,所以我接受它。谢谢我拒绝了播放器。插座。在。。。构图,因为它看起来没有我的品味优雅。我想让我的玩家看起来像是直接发射和接收事件。@VincentPride:是的,在你发表评论前几分钟,我刚刚在答案中添加了一条关于Player.prototype的注释,你可能没有看到它。:-很高兴第二种选择有效!:-ECMA-262 4.2.1:每个构造函数都是一个具有名为prototype的属性的函数,该属性用于实现基于prototype的inheritance@T.J.Crowder读完之后,我也同意作者的观点,即“原型遗传”一词用词不当。在经典继承中,当一个类从另一个类继承时,这些方法和属性被复制到新的子类中。然而,动态是完全不同的。在实例上查找属性,如果找不到,则将查找委托给原型对象。@wmock:将继承一词与原型继承结合使用已经非常成熟,有35年的历史,早于JavaScript。如果你愿意的话,你可以和风赛跑,但如果你想发挥最大的作用,我会把它排除在答案之外不是我的dv@T.J.Crowder我认为我们在如何解决这个问题上是一致的:现在的问题是我们是否都同意所用术语的语义——对各自而言!规范使用术语原型继承。I DV
因为这意味着您需要提供重要的证据来支持您的断言,即JS中不存在继承。如果你说:JS中没有经典继承,那么很公平:ECMA-262 4.2.1:每个构造函数都是一个函数,它有一个名为prototype的属性,用于实现基于prototype的inheritance@T.J.Crowder读完之后,我也同意作者的观点,即“原型遗传”一词用词不当。在经典继承中,当一个类从另一个类继承时,这些方法和属性被复制到新的子类中。然而,动态是完全不同的。在实例上查找属性,如果找不到,则将查找委托给原型对象。@wmock:将继承一词与原型继承结合使用已经非常成熟,有35年的历史,早于JavaScript。如果你愿意的话,你可以和风赛跑,但如果你想发挥最大的作用,我会把它排除在答案之外不是我的dv@T.J.Crowder我认为我们在如何解决这个问题上是一致的:现在的问题是我们是否都同意所用术语的语义——对各自而言!规范使用术语原型继承。因为这意味着您需要提供重要的证据来支持您的断言,即JS中不存在继承。如果你说:JS中没有古典继承这类东西,那么很公平:你是对的,在我的情况下,合成会更酷,因为我只使用on和emit。但是想象一下,有一个巨大的超类,其中有几十个方法,你必须在你的子类中重新绑定。@VincentPride JS还没有类——尽管我知道你的意思。但是关于一个巨大的物体和重新绑定的部分,我不明白。你是对的,在我的情况下,合成会更酷,因为我只使用on和emit。但是想象一下,有一个巨大的超类,其中有几十个方法,你必须在你的子类中重新绑定。@VincentPride JS还没有类——尽管我知道你的意思。但是关于一个巨大的物体和重新绑定的部分,我不明白。