正确的JavaScriptinheritance
我一直在阅读很多有关JavaScript的“inheritance”的文章。 其中一些使用new
而其他人推荐Object.Create
。 我读得越多,我就越感到困惑,因为它似乎存在无数的变种来解决inheritance问题。
有人可以善意地向我展示最可接受的方式(或事实上的标准,如果有的话)?
(我想有一个基本对象Model
,我可以扩展RestModel
或LocalStorageModel
。)
简单: Object.create
在所有环境中都不被支持,但是可以用new
方式来擦除。 除此之外,两者有不同的目的: Object.create
只是创build一个从另一个inheritance的对象,而new
也调用一个构造函数。 使用适当的东西。
在你的情况下,你似乎希望RestModel.prototype
从Model.prototype
inheritance。 因为你不想创build一个新的实例(实例化一个new Model
),并且不想调用模型构造函数, Object.create
(或者它的shim)是正确的方法。
RestModel.prototype = Object.create(Model.prototype);
如果你想在RestModels上调用Model构造函数,那和原型没有任何关系。 使用call()
或apply()
:
function RestModel() { Model.call(this); // apply Model's constructor on the new object ... }
JavaScript是基于原型而不是类的OOP语言。 这是你可以看到的所有这些混乱的原因之一:很多开发人员使用JS,因为它是基于类的。
因此,你可以看到很多试图在JS中使用基于类的语言范例的库。
另外,JS本身缺乏一些使得inheritance的特性,也是原型的,痛苦的。 例如,在Object.create
之前,没有办法从另一个对象创build一个对象(就像一个基于OOP原型的语言应该这样做),但只能使用一个function
作为构造函数。
而且也是因为这个原因,你可以看到很多图书馆试图“固定”语言,每一个都以自己的方式。
所以,你的困惑是正常的。 说,“官方”方式是使用prototype
属性, function
s作为构造函数, Object.create
从对象的inheritance来创build。 但是,如上所述,在某些情况下代码是冗长的或者缺乏function性的,所以这个程序经常被包装起来,然后就变成了个人品味的问题。
因为你正在谈论模型,你可以看看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();
现在,这里的问题:
- 只有在设置了
RestModel.prototype
时,Model
的构造函数才被调用一次。 因此,每个RestModel
实例的foo
属性将是“foo”。 - 不仅如此,而且所有
RestModel
实例将共享this.array
属性中相同数组的相同实例。 如果直接创buildModel
实例,则每个实例都有一个新的数组实例。 -
myModel.constructor
是Model
而不是RestModel
。 这是因为我们用一个新的Model
实例覆盖prototype
,其中包含的constructor
等于Model
。 - 如果你想通过对构造函数的懒惰调用来获得
RestModel
的新实例,是不可能的。
我认为有一些要点,当然不是唯一的。 那么,如何解决这个问题呢? 正如我所说,很多人已经这样做了,他们创build图书馆和框架也限制了详细程度。 然而,有一个想法:
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);
注意,如果你不想使用prototype
或function
作为构造函数,用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();
在这种情况下,你基本上是模仿构造函数。 您也可以将descriptor
传递给Object.create
(请参阅文档 ),但它变得更加冗长。 大多数人使用某种extend
函数或方法(正如您在Backbone示例中可能看到的那样)。
希望它有帮助!