我一直在阅读很多关于 javascript 中“继承”的文章。他们中的一些人使用new
,而其他人则推荐Object.Create
。我读得越多,我就越困惑,因为它似乎存在无穷无尽的变体来解决继承问题。
有人可以向我展示最接受的方式(或事实上的标准,如果有的话)?
(我想要一个Model
可以扩展的基础对象RestModel
或LocalStorageModel
。)
我一直在阅读很多关于 javascript 中“继承”的文章。他们中的一些人使用new
,而其他人则推荐Object.Create
。我读得越多,我就越困惑,因为它似乎存在无穷无尽的变体来解决继承问题。
有人可以向我展示最接受的方式(或事实上的标准,如果有的话)?
(我想要一个Model
可以扩展的基础对象RestModel
或LocalStorageModel
。)
JavaScript 是一种基于原型而非类的 OOP 语言。这是造成所有这些混乱的原因之一:许多开发人员使用 JS,因为它是基于类的。
因此,您可以看到很多库试图在 JS 中使用基于类的语言范式。
此外,JS 本身缺乏一些特性,这使得继承,也是基于原型的,很痛苦。例如,在Object.create
没有办法从另一个对象创建对象之前(就像基于 OOP 原型的语言应该做的那样),而只能使用function
as 构造函数。
出于这个原因,你可以看到很多库都在尝试“修复”该语言,每个库都以自己的方式。
所以,你的困惑是正常的。也就是说,“官方”的方式是使用prototype
属性,function
s作为构造函数,并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();
现在,这里的问题:
Model
被调用一次,只有当RestModel.prototype
设置了。因此,foo
每个RestModel
实例的属性都是“foo”。RestModel
实例都将在this.array
属性中共享同一个数组的同一个实例。如果Model
直接创建一个实例,则每个实例都有一个新的数组实例。myModel.constructor
是Model
而不是RestModel
。那是因为我们prototype
用一个新的 , 实例覆盖Model
,它包含constructor
等于Model
。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);
请注意,如果您不想使用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();
在这种情况下,您基本上模仿了构造函数。您也可以将 a 传递descriptor
给Object.create
(请参阅文档),但它会变得更加冗长。大多数人使用某种extend
函数或方法(正如您可能在 Backbone 示例中看到的那样)。
希望能帮助到你!
简单: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
...
}