防止构造函数的多个实例共享相同的原型属性

Preventing multiple instances of a constructor function from sharing the same prototype properties

本文关键字:原型 属性 共享 构造函数 实例      更新时间:2023-09-26

正当我认为我掌握了JavaScript中原型继承的工作原理时,我遇到了一个我以前没有考虑过的问题。

请看下面的简单JavaScript代码:

var Observable = function () {
  this.events = [];
};
Observable.prototype.addEvent = function (e) {
  this.events.push(e);
};
var Model = function () {};
Model.prototype = new Observable();
var appModel = new Model();
var taskModel = new Model();
appModel.addEvent('Hello');
taskModel.addEvent('World');

查看appModel.eventstaskModel.events都会得到相同的数组:['Hello', 'World']。我要做的是让每个新的Model有自己的events数组尽可能干净的方式。Model的工作原理如下:

var Model = function () {
  this.events = [];
};
Model.prototype = new Observable();

然而,随着更多的属性被添加到Observable,这变得更加笨拙。我想我可以这样修改:

var Model = function () {
  this.prototype.constructor.apply(this, arguments);
};
Model.prototype = new Observable();

虽然我相信你们中有JavaScript经验的人意识到这会抛出一个错误:TypeError: Cannot read property 'constructor' of undefined .

总之,我正在寻找一种方法,让每个新的ModelObservable继承属性,每个Model都有自己的events。我意识到这是非常类,但我想知道如何做到这一点,只使用基于JavaScript原型的继承。

值得注意的是,我看过Dean Edward的Base.js。以下作品:

var Observable = Base.extend({
  constructor: function () {
    this.events = [];
  },
  addEvent: function (e) {
    this.events.push(e);
  }
});
var Model = Observable.extend({
  constructor: function () {
    this.base();
  }
});
var appModel = new Model();
var taskModel = new Model();
appModel.addEvent('Hello');
taskModel.addEvent('World');

但是下面的:

var Observable = Base.extend({
  events: [],
  addEvent: function (e) {
    this.events.push(e);
  }
});
var Model = Observable.extend({
  constructor: function () {
    this.base();
  }
});
var appModel = new Model();
var taskModel = new Model();
appModel.addEvent('Hello');
taskModel.addEvent('World');

除此之外,我还想学习如何使用不使用类库的JavaScript原型来做到这一点。

我在这里理解的是,您希望每个实例都有它们单独的事件数组,如果是这样,请遵循答案:

function Model(){
  this.events = [];
  this.addEvent = function(eventName){
    this.events.push(eventName);
  };
  this.getEvents = function(){
    return this.events;
  }
}
var m1 = new Model;
var m2 = new Model;
m1.addEvent("eve-1");
m2.addEvent("eve-2");
m1.getEvents(); //["eve-1"]
m2.getEvents(); //["eve-2"]

在您的情况下,您直接将事件添加到prototype而不是实例,因此它们被添加到所有实例中…我希望这对你有帮助

我曾尝试过这个来解决我的问题:

var Model = function () {
  this.prototype.constructor.apply(this, arguments);
};
Model.prototype = new Observable();

实际的解决方案是这样做:

var Model = function () {
  Model.prototype.constructor.apply(this, arguments);
};
Model.prototype = new Observable();

这将给每个Model它自己的events数组,以及给每个Model Observable构造函数创建的任何其他属性。这里的小开销是Model.prototype.events是一个永远不会被访问的空数组。

这里的第一个代码位导致Model的每个实例都有一个共同的events数组的原因是因为它们对Observable的单个公共实例有一个原型引用。换句话说,通过

Model.prototype = new Observable();

你基本上是告诉JavaScript"让Model的所有实例都像这个特定的Observable实例"。

在JavaScript中,有几种不同的方法可以正确地执行原型继承。如果您可以使用最近标准化的ECMAScript 6,您可以使用为此目的而设计的方便的新classextends关键字。
class Observable {
    constructor() {
        this.events = [];
    }
    addEvent() {
        this.events.push(e);
    }
}
class Model extends Observable {
    // Model definition
};

遗憾的是,对ECMAScript 6的支持仍然不是很好。ECMAScript 5添加了对象。(至少就我所知,)

var Observable = function () {
    this.events = [];
};
Observable.prototype.addEvent = function (e) {
    this.events.push(e);
};
var Model = function () {
    Observable.call(this);
};
Model.prototype = Object.create(Observable.prototype);

MDN有一篇很好的关于JavaScript中使用这种技术的OOP的文章