13

我一直在阅读很多关于 javascript 中“继承”的文章。他们中的一些人使用new,而其他人则推荐Object.Create。我读得越多,我就越困惑,因为它似乎存在无穷无尽的变体来解决继承问题。

有人可以向我展示最接受的方式(或事实上的标准,如果有的话)?

(我想要一个Model可以扩展的基础对象RestModelLocalStorageModel。)

4

2 回答 2

11

JavaScript 是一种基于原型而非类的 OOP 语言。这是造成所有这些混乱的原因之一:许多开发人员使用 JS,因为它是基于类的。

因此,您可以看到很多库试图在 JS 中使用基于类的语言范式。

此外,JS 本身缺乏一些特性,这使得继承,也是基于原型的,很痛苦。例如,在Object.create没有办法从另一个对象创建对象之前(就像基于 OOP 原型的语言应该做的那样),而只能使用functionas 构造函数。

出于这个原因,你可以看到很多库都在尝试“修复”该语言,每个库都以自己的方式。

所以,你的困惑是正常的。也就是说,“官方”的方式是使用prototype属性,functions作为构造函数,并Object.create从对象的实例中创建。但是,如上所述,在某些情况下代码很冗长或缺少功能,因此此过程通常以某种方式包装,然后就变成了个人喜好问题。

因为您在谈论模型,所以您可以查看Backbone 的模型,看看这种方法是否适合您。

更新:举一些例子,我希望能帮助你澄清,这里使用的是老派继承方式new

function Model() {
    this.foo = "foo";
    this.array = [];
}

Model.prototype.foo = "";
Model.prototype.array = null;
Model.prototype.doNothing = function() {};

function RestModel() {
    this.bar = "bar";
}

RestModel.prototype = new Model;
RestModel.prototype.bar = ""

var myModel = new RestModel();

现在,这里的问题:

  1. 的构造函数Model被调用一次,只有当RestModel.prototype设置了。因此,foo每个RestModel实例的属性都是“foo”。
  2. 不仅如此,所有 RestModel实例都将在this.array属性中共享同一个数组的同一个实例。如果Model直接创建一个实例,则每个实例都有一个新的数组实例。
  3. myModel.constructorModel而不是RestModel。那是因为我们prototype用一个新的 , 实例覆盖Model,它包含constructor等于Model
  4. 如果你想拥有一个RestModel对构造函数进行惰性调用的新实例,是不可能的。

我认为有一些要点,当然它们不是唯一的。那么,如何解决这些问题呢?正如我所说,很多人已经这样做了,他们创建库和框架也是为了限制冗长。但是,要有一个想法:

function Model() {
    this.foo = "foo";
    this.array = [];
}

Model.prototype.foo = "";
Model.prototype.array = null;
Model.prototype.doNothing = function() {};

function RestModel() {
    Model.call(this);

    this.bar = "bar";
}

RestModel.prototype = Object.create(Model.prototype);
RestModel.prototype.constructor = RestModel;
RestModel.prototype.bar = ""

var myModel = new RestModel();

// for lazy constructor call
var anotherModel = Object.create(RestModel.prototype);

RestModel.call(anotherModel);

请注意,如果您不想使用prototypefunction作为构造函数,Object.create您可以这样做:

var Model = {
    foo: "",
    array: null,
    doNothing: function() {}
}

var RestModel = Object.create(Model);

RestModel.bar = "";

var myModel = Object.create(RestModel);

// Assuming you have to set some default values
initialize(RestModel);

或者:

var Model = {
    foo: "",
    array: null,
    doNothing: function() {},

    init : function () {
        this.foo = "foo";
        this.array = [];
    },
}

var RestModel = Object.create(Model);

RestModel.bar = "";
RestModel.init = function () {
    Model.init.call(this);

    this.bar = "bar";
}

var myModel = Object.create(RestModel);

myModel.init();

在这种情况下,您基本上模仿了构造函数。您也可以将 a 传递descriptorObject.create(请参阅文档),但它会变得更加冗长。大多数人使用某种extend函数或方法(正如您可能在 Backbone 示例中看到的那样)。

希望能帮助到你!

于 2012-06-05T14:14:18.280 回答
11

简单:Object.create并非在所有环境中都支持,但可以用new. 除此之外,两者有不同的目标:Object.create只是创建一个继承自其他对象的对象,同时调用构造函数。使用合适的。new

在您的情况下,您似乎希望RestModel.prototype继承自Model.prototype. Object.create(或其垫片)是正确的方法,因为您不想 a)创建一个新实例(实例化 a new Model)和 b)不想调用 Model 构造函数:

RestModel.prototype = Object.create(Model.prototype);

如果你想在 RestModels 上调用 Model 构造函数,那与原型无关。使用call()or apply()

function RestModel() {
    Model.call(this); // apply Model's constructor on the new object
    ...
}
于 2012-06-05T14:06:08.587 回答