我试图理解Javascript中的继承。
我知道每个对象都有一个原型,这是它继承属性的对象。我知道 .prototype
属性只存在于函数上,当它用作构造函数时,它是将设置为从该函数创建的对象的原型
我知道对象的原型是无法访问的,尽管某些浏览器支持 __proto__
属性。(但由于它不是语言的"经典"部分,我想了解如何在没有它的情况下使用该语言)。
因此,如果所有这些都是正确的(?),我想了解定义继承链的标准方法是什么。
我能想到的唯一方法是:
我希望它们从另一个对象继承的所有对象都必须通过构造函数创建。它们的"基对象"将被设置为其构造函数的.prototype
。
当我希望其中一个对象成为其他对象的"基对象"时,我会将其设置为另一个构造函数的.prototype
。等等。
这似乎很奇怪。有没有办法(在"普通"JS中)直接设置对象的"基础"?还是我必须以上述方式使用构造函数才能创建继承链?
创建继承的"标准"方法是什么?我描述的方法是否是标准方法?
JavaScript 支持继承的主要方式是通过原型继承。具体来说,每当在初始对象上找不到属性查找时,JavaScript 中的对象就会委托给其他对象。此委托一直持续到JavaScript引擎到达找到属性或引发错误的Object.prototype
。
使用特定对象作为原型创建对象的当前最佳实践是使用 Object.create
- 您可以在此处查看更多信息。
下面是一个示例:
var methods = {
method1: function () { console.log( 'something' ); },
method2: function () { return 'cool'; }
};
/*
* Now firstObj will delegate to methods whenever a property lookup can't
* be found on firstObj itself
*/
var firstObj = Object.create( methods );
// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property';
/*
* You can create a chain of delegations! Property lookup first happens on secondObj.
* If its not found there, it looks up the property in firstObj. If its not found there,
* then it looks up the property in methods. Finally, if not found, it tries
* Object.prototype
*/
var secondObj = Object.create ( firstObj );
JavaScript 中的继承起初有点难以理解,因为:
- JavaScript是一种原型的面向对象的编程语言(即对象直接从其他对象继承)。这意味着类和对象之间没有区别。用作类的对象称为原型。
- 不幸的是,创建原型实例的传统方法是使用
new
(这让人们认为实例继承自构造函数,而不是原型)。这被称为构造函数模式,它是JavaScript中混淆的主要原因。
因此,引入了Object.create
。它允许对象直接从其他对象继承。但是,与使用Object.create
相比,new
很慢。我遇到了和你一样的问题,我正在寻找替代方案;我确实想出了一个。
JavaScript 中 OOP 的传统方式
请考虑以下代码:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = new Person;
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
这种编写代码的方式存在以下几个问题:
- 没有封装。构造函数和原型方法到处都是定义的。它看起来语无伦次。比如沙格蒂。它看起来不像一个逻辑单元。
- 我们通过将
Man.prototype
设置为Person.prototype
来继承new Person
。但是,在这样做的过程中,我们正在初始化firstname
,lastname
和gender
属性Man.prototype
这是错误的。
JavaScript 中的 OOP 新方式
随着Object.create
的引入,我们现在可以编写如下代码:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
唯一的变化是我们写Man.prototype = Object.create(Person.prototype)
而不是Man.prototype = new Person
。这就解决了传统方法的第二个问题。但是,代码看起来仍然像意大利面条。
但是,Object.create
非常强大。您还可以使用它来编写面向对象的代码,而无需创建构造函数。有些人称之为初始值设定项模式:
var person = {
init: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
};
var man = Object.create(person, {
init: {
value: function (firstname, lastname) {
person.init.call(this, firstname, lastname, "M");
}
}
});
var bobMarley = Object.create(man);
bobMarley.init("Bob", "Marley");
alert(bobMarley.getFullname());
这解决了传统方法的所有问题。但是,它也引入了一些自己的新问题:
- 创建
- 原型实例的方式与创建对象文本的方式不一致。
- 您必须使用
Object.create
创建一个实例,然后使用init
初始化新对象。这比简单地使用new
慢得多。
我的OOP方式是JavaScript
为了解决这个问题,我用JavaScript为OOP编写了自己的函数:
var Person = defclass({
constructor: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
});
var Man = extend(Person, {
constructor: function (firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
});
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, properties) {
var prototype = Object.create(constructor.prototype);
var keys = Object.keys(properties);
var length = keys.length;
var index = 0;
while (index < length) {
var key = keys[index++];
prototype[key] = properties[key];
}
return defclass(prototype);
}
我在 JavaScript 中为 OOP 定义了两个函数defclass
和 extend
。defclass
函数从原型创建一个"类"。这是可能的,因为原型和类是同构的。
扩展函数用于继承。它创建一个constructor
prototype
的实例,并在返回新原型的"类"之前将一些属性复制到该实例上。
这是我目前在 JavaScript 中创建原型链的方式。与其他方法相比,它具有以下优点:
- 每个"类"都被封装了。没有到处晃来晃去的原型方法。它看起来不像意大利面。
extend
函数使用Object.create
进行继承。因此,不会向新原型添加额外的属性。这是一个空白的原型。- 您不必担心重置
prototype
上的constructor
属性。它是自动为您完成的。 defclass
和extend
函数是一致的,这与初始值设定项模式中的对象文本和Object.create
函数不同。- 我们使用
new
而不是Object.create
和init
创建实例。因此,生成的代码要快得多。
我现在可能是错的,但我不这么认为。希望有帮助。
你可以在javascript中通过2种方式继承 - 经典和原型
古典
function inherit (C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}
原型
function inherit (o) {
function F() {}
F.prototype = o;
return new F();
}