我已经阅读了很多有关javascript中“继承”的文章。其中一些使用new,而其他一些则建议使用Object.Create。我读得越多,我就越困惑,因为它似乎存在无穷无尽的变种来解决继承问题。

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

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

#1 楼

简单:并非在所有环境中都支持Object.create,但可以使用new进行填充。除此之外,两者有不同的目的:Object.create只是创建一个从其他对象继承的对象,而new也调用构造函数。使用适当的方法。

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

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


如果要在RestModels上调用Model构造函数,则与原型无关。使用call()apply()来实现以下目的:

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


评论


在这种情况下,为什么Object.Create比new更好?如果我也想支持构造函数怎么办?并且在RestModel和Model中都调用了该构造函数(如果我创建了RestModel)。我通常是C#开发人员,所以我并没有真正的区别。我想定义基本原型并使用构造函数,但我不明白如何同时获得两者?

– jgauffin
2012年6月5日14:11



我认为这个答案可以概括为“使用适当的内容”。有时您需要构造函数中的逻辑,有时则不需要。 +1

–user1106925
2012年6月5日14:13

#2 楼

JavaScript是一种基于原型而非类的OOP语言。这就是引起这种混乱的原因之一:许多开发人员都使用JS,因为它是基于类的。

因此,您可以看到很多试图使用的库。

此外,JS本身缺乏使继承(也基于原型)痛苦的某些特征。例如,在Object.create之前,没有一种方法可以从另一个对象创建对象(就像基于OOP的基于原型的语言一样),而只能使用function作为构造函数。

原因,您会看到很多试图以自己的方式“修复”该语言的库。

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

因为您在说关于模型,您可以看看Backbone的模型,看看这种方法是否适合您。

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();


现在,这里的问题是:仅在设置new时,一次调用Model的构造函数。因此,每个RestModel.prototype实例的foo属性将为“ foo”。
不仅如此,所有RestModel实例还将在RestModel属性中共享同一阵列的同一实例。如果直接创建this.array的实例,则每个实例都有一个新的array实例。

ModelmyModel.constructor而不是Model。这是因为我们用新的RestModel实例覆盖了prototype,其中包含Model等于constructor的新实例。
如果您想通过延迟构造函数的方式来调用Model的新实例,那是不可能的。

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

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);


请注意,如果您不想使用RestModelprototype作为构造函数,则可以使用function来做到这一点:

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();


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

希望对您有所帮助!

评论


您建议我使用哪个示例?

– jgauffin
2012年6月5日17:34

所有这些都是有效的,但是以某种形式或另一种形式它们最终将变得冗长。例如,您可以对RestModel是否具有很多新的属性或方法进行成像。这就是我说JS仍然缺少某些东西时所说的。为了消除冗长,或者您决定使用第三方库,或者创建几个实用程序函数来执行此操作。我将避免使用纯粹的新方法(第一种),而只是决定第二种还是第三种,这取决于您是否对“类似于”“类”(第二种)(第三种)感到更舒服。

– ZER0
2012年6月5日17:49