我正在阅读RxJS4的源代码并遇到了继承(https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/internal/util.js)的函数:
var inherits = Rx.internals.inherits = function (child, parent) {
function __() { this.constructor = child; }
__.prototype = parent.prototype;
child.prototype = new __();
};
我花了一些时间搞清楚原型链,并认为该函数确实使我们能够'连接'子和父,并创建一个带继承的子对象。但我也注意到如果我创建一个父对象,它的构造函数,它的构造函数将链接到子函数。我是否错误地理解了这个函数(这是一种正确的继承方式,我将对象链接错误了)?
答案 0 :(得分:1)
在javascript中你必须习惯的一件事是构造函数是类。虽然ES6确实引入了class
关键字和类语法,但它只是底层机制的语法糖。架构没有改变。
原型是构造函数的属性,构造函数将使用它来实例化新对象。所以,例如,如果你想创造很多“人”,你会写下这样的东西:
function Person (name) {
this.name = name;
}
Person.prototype = {};
Person.prototype.name = "";
Person.prototype.age = null;
Person.prototype.gender = null;
注意一些事情。与其他OO语言相比,这基本上是颠倒的。在其他语言中,您可以定义一个类,然后可以将构造函数定义为类的特殊属性/方法。在javascript中,您定义了一个构造函数,然后将类(原型)定义为该构造函数的特殊属性。
其次,没有特殊的constructor
关键字。构造函数只是一个常规函数。没有什么特别之处。如果您使用new
关键字调用它,它只会成为构造函数:
var x = Person(); // regular function, x is undefined
var y = new Person(); // constructor, y is an instance of Person
所以,因为没有任何东西可以告诉程序员天气,函数是构造函数或常规函数javascript程序员开发了一种约定,其中函数名称始终以小写字母开头,构造函数名称始终以大写字母开头。从上面的代码中可以看到名为Person
的函数,因此您可以假设我打算将它作为构造函数。
由于原型是......对象的原型,然后从构造函数继承,您将子原型设置为Parent原型的实例。在现代JS中你会这样做:
function Employee (name, job) {
this.name = name;
this.job = job;
}
Employee.prototype = Object.create(Person.prototype); // Inherit!
Employee.prototype.job = null;
请注意,我们从一个对象(原型)继承,因为在javascript中你继承自对象,而不是构造函数而不是类。
其次,请注意我们通过将原型设置为父项原型的副本来继承。这是因为如果我们只是将父节点的原型分配给我们自己,那么当我们向它添加新属性时(比如本例中的job
)我们不想修改父节点的原型(因为那样就不会继承)。
在Object.create
功能存在之前的几天内,您需要执行此操作:
Employee.prototype = new Person();
即使Object.create
通常是首选,但今天仍然有效。所以在RxJS代码中看到这个:
child.prototype = new __();
这就是继承发生的地方。请记住,原型是构造函数用作创建新对象的模板的原因。所以上面一行:
__.prototype = parent.prototype;
意味着我们现在有一个函数__
,它将创建一个类似于父对象创建的对象的对象。所以做new __()
会创建一个类似于调用父构造函数但不执行父构造函数中定义的任何逻辑的对象。所以基本上它正在做类似于Object.create(parent)
;
继承只是将父级原型的副本分配给我们自己的原型。上面所有其他复杂的部分只是准备复制父级的原型。
答案 1 :(得分:1)
但我也注意到如果我创建一个父对象,它的构造函数,它的构造函数将链接到子函数。
这不正确。原型链是单向的,父级不会以任何方式修改。
这个实现的唯一警告是,当调用child的构造函数时,不会调用父的构造函数。 ES6类实现强制执行。
在你的图表中有几个问题:
__
的实例没有指向__
函数的构造函数属性。实际上,__proto__
存在的原因是将构造函数属性设置为子构造函数。
子实例的__
将是new __()
的实例(请注意继承代码中的__proto__
),parent.prototype
的实例将为constructor
1}}。
因此,inherits实用程序在原型链中注入一个元对象,其唯一目的是确保function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
属性指向子实例中的正确类。
将这与babel实现ES6类语义的方式进行对比可能具有教育意义:
propertiesObject
它利用Object.create采用第二个constructor
参数的事实,该参数可用于添加额外的属性,在这种情况下恰好是Object.create
。 __
返回的对象与RxJS代码中new __()
返回的{{1}}实例的用途相同。