正确的javascript继承

jga*_*fin 13 javascript

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

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

(我想有一个基础物体Model,我可以延伸RestModelLocalStorageModel).

Ber*_*rgi 11

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

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

RestModel.prototype = Object.create(Model.prototype);
Run Code Online (Sandbox Code Playgroud)

如果要在RestModels上调用Model构造函数,则与原型无关.使用call()apply()为此:

function RestModel() {
    Model.call(this); // apply Model's constructor on the new object
    ...
}
Run Code Online (Sandbox Code Playgroud)


ZER*_*ER0 11

JavaScript是一种基于原型而不是类的OOP语言.这是你可以看到的所有这些混乱的原因之一:许多开发人员使用JS,因为它是基于类的.

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

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

而且也是因为这个原因,你可以看到很多图书馆试图以自己的方式"修复"语言.

所以,你的困惑是正常的.比方说,"官方"方式是使用prototype属性,functions作为构造函数,并Object.create从对象的insteance创建.然而,如上所述,在某些情况下,代码是冗长的或缺乏功能,因此这个过程通常以某种方式包装,然后它成为个人品味的问题.

因为你在谈论模型,你可以看看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();
Run Code Online (Sandbox Code Playgroud)

现在,这里的问题:

  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);
Run Code Online (Sandbox Code Playgroud)

请注意,如果您不想使用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);
Run Code Online (Sandbox Code Playgroud)

要么:

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();
Run Code Online (Sandbox Code Playgroud)

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

希望能帮助到你!