简体   繁体   English

正确的继承方法

[英]Right way to do Inheritance

I was reading the source code of RxJS4 and came across the function which does inheritance ( https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/internal/util.js ): 我正在阅读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 __();
};

I spent some time figuring out the prototype chains and think the function does enable us to do 'connect' the child and parent and create a child object with inheritance. 我花了一些时间弄清楚原型链,并认为该函数确实使我们能够“连接”孩子和父母,并创建具有继承性的孩子对象。 But I also noticed if I create a parent object, its constructor, its constructor will be linked to the child function. 但是我也注意到,如果我创建一个父对象,它的构造函数,其构造函数将链接到子函数。 Am I understand this function incorrectly (that this is a correct way to do inheritance and I'm linking the objects wrong)? 我是否对该功能的理解不正确(这是进行继承的正确方法,并且我将对象链接错误)?

在此处输入图片说明

Objects and Constructors 对象和构造函数

One thing you have to get used to in javascript is that the constructor is the class. 您必须在javascript中习惯的一件事是构造函数是类。 While ES6 did introduce the class keyword and the class syntax it is merely a syntax sugar of the underlying mechanism. 尽管ES6确实引入了class关键字和class语法,但它仅仅是底层机制的语法糖。 The architecture was not changed. 架构未更改。

A prototype is a property of constructors that the constructor will use to instantiate new objects. 原型是构造函数的属性,构造函数将使用该原型来实例化新对象。 So, for example if you want to create lots of "people" you'd write something like this: 因此,例如,如果您想创建很多“人”,则可以这样编写:

function Person (name) {
    this.name = name;
}

Person.prototype = {};

Person.prototype.name = "";
Person.prototype.age = null;
Person.prototype.gender = null;

Notice a few things. 注意几件事。 This is basically upside-down compared to other OO languages. 与其他OO语言相比,这基本上是颠倒的。 In other languages you'd define a class and you can then define a constructor as a special property/method of the class. 在其他语言中,您将定义一个类,然后可以将构造函数定义为该类的特殊属性/方法。 In javascript you define a constructor and then you define the class (prototype) as a special property of that constructor. 在javascript中,您定义一个构造函数,然后将类(原型)定义为该构造函数的特殊属性。

Second, there is not special constructor keyword. 第二,没有特殊的constructor关键字。 A constructor is just a regular function. 构造函数只是一个常规函数。 There is nothing really special about it. 真的没有什么特别的。 It only becomes a constructor if you call it using the new keyword: 仅当使用new关键字调用它时,它才成为构造函数:

var x = Person(); // regular function, x is undefined
var y = new Person(); // constructor, y is an instance of Person

So, because there is nothing that can tell a programmer weather a function is a constructor or a regular function javascript programmers have developed a convention where function names always begin with lower-case and constructor names always begin with upper-case. 因此,因为没有什么可以告诉程序员的,所以函数是构造函数还是常规函数,因此javascript程序员已经制定了一种约定,即函数名称始终以小写字母开头,而构造函数名称始终以大写字母开头。 From the code above you see the function named Person so you can assume that I intend it to be a constructor. 从上面的代码中,您可以看到名为Person的函数,因此可以假定我打算将其用作构造函数。

Inheritance 遗产

Since a prototype is, well... the prototype of an object, then to inherit from a constructor you set the child prototype to an instance of the Parent's prototype. 因为原型是对象的原型,所以要从构造函数继承,您可以将子原型设置为Parent原型的实例。 In modern JS you'd do this: 在现代JS中,您可以这样做:

function Employee (name, job) {
    this.name = name;
    this.job = job;
}

Employee.prototype = Object.create(Person.prototype); // Inherit!

Employee.prototype.job = null;

Notice that we inherit from an object (the prototype) because in javascript you inherit from objects, not constructors and not classes. 注意,我们从对象(原型)继承,因为在javascript中,您从对象而不是构造函数而不是类继承。

Second, notice that we inherit by setting our prototype to a copy of our parent's prototype. 其次,请注意,我们通过将原型设置为父代原型的副本来继承。 This is because if we merely assign our parent's prototype to our own then when we add new properties to it (like job in this example) we don't want to modify our parent's prototype (because then that would not be inheritance). 这是因为如果仅将父级的原型分配给我们自己的原型,那么当我们向其添加新属性时(如本例中的job ),我们就不想修改父级的原型(因为那样就不会继承)。

In the days before the Object.create function existed, you'd instead do this: Object.create函数存在之前,您应该这样做:

Employee.prototype = new Person();

This is still valid today even though Object.create is generally preferred. 即使通常首选Object.create今天它仍然有效。 So in the RxJS code when you see this: 因此,在RxJS代码中,您将看到以下内容:

child.prototype = new __();

That's where inheritance happens. 那就是继承发生的地方。 Remember, the prototype is what a constructor use as a template to create a new object. 请记住,原型是构造函数用作模板创建新对象的对象。 So the line above: 所以上面的行:

__.prototype = parent.prototype;

Means we now have a function __ which will create an object similar to the object the parent would have created. 意味着我们现在有一个函数__ ,它将创建一个与父代将创建的对象相似的对象。 So doing new __() would create an object similar to calling the parent constructor but without executing any logic defined in the parent constructor. 因此,执行new __()将创建类似于调用父构造函数的对象,但不执行在父构造函数中定义的任何逻辑。 So basically it's doing something similar to Object.create(parent) ; 因此,基本上,它所做的事情类似于Object.create(parent) ;

The inheritance is merely assigning a copy of the parent's prototype to our own prototype. 继承只是将父代原型的副本分配给我们自己的原型。 All the other complicated bits above is merely preparing for copying the parent's prototype. 上面所有其他复杂的步骤仅是为复制父级的原型做准备。

But I also noticed if I create a parent object, its constructor, its constructor will be linked to the child function. 但是我也注意到,如果我创建一个父对象,它的构造函数,其构造函数将链接到子函数。

This is not correct. 这是不正确的。 The prototype chain goes one way, and the parent is not modified in any way. 原型链采用一种方式,并且父级链没有任何修改。

The only caveat with this implementation is that the parent's constructor does not get called when child's constructor is called. 此实现的唯一警告是,在调用子级的构造函数时不会调用父级的构造函数。 The ES6 class implementation enforces that. ES6类的实现会强制执行该操作。

In your diagram there are a couple issues: 在您的图中有几个问题:

  1. The instance of __ does not have a constructor property pointing to __ function. __的实例没有指向__函数的构造函数属性。 In fact the very reason __ exists is to set the constructor property to child constructor. 实际上__存在的根本原因是将构造函数属性设置为子构造函数。

  2. The __proto__ of child instance will be instance of __ (Note the new __() in the inherits code) and the __proto__ of that will be parent.prototype . 子实例的__proto__将是__实例(请注意继承代码中的new __() ),而其__proto__将是parent.prototype

So, the inherits utility injects a meta-object in the prototype chain, whose only purpose is to ensure that constructor property points to correct class in child instance. 因此,继承实用程序会在原型链中注入一个元对象,其唯一目的是确保constructor属性指向子实例中的正确类。

It may be educational to contrast this with how babel implements ES6 class semantics: 将此与babel如何实现ES6类语义进行对比可能是有益的:

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

It leverages the fact that Object.create takes a second propertiesObject argument which can be utilized to add an extra property which in this case happens to be constructor . 它利用了Object.create具有第二个propertiesObject参数的事实,该参数可用于添加一个额外的属性,在这种情况下,该属性恰好是constructor The object returned by Object.create serves the same purpose as the __ instance returned by new __() in the RxJS code. Object.create返回的对象的用途与RxJS代码中new __()返回的__实例的用途相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM