JS foundation – master all aspects of inheritance

Time:2020-1-14

Preface

In the previous article, we analyzed the related knowledge points of prototype and prototype chain in detail. This article is about the inheritance closely related to prototype chain. It is a very important knowledge point in the front-end foundation. It is very useful for code reuse. In this article, we will analyze various inheritance methods, advantages and disadvantages in JS in detail. I hope that after reading this article, we can understand the inheritance and related conceptual principles The solution is more thorough.

This article needs to understand prototype, prototype chain andcallKnowledge of:

JS foundation – relationship between function, object and prototype, prototype chain

JS basic – interviewer wants to know how well you understand call, apply, bind?

What is inheritance?

Wikipedia: inheritance allows a subclass to have various properties and methods of its parent without having to write the same code again.

Inheritance is the process by which a class obtains methods and properties from another class

PS: or multiple classes

The principle of JS implementation inheritance

Remember this concept, you will find that inheritance in JS is to achieve this purpose, and the difference is that they are implemented in different ways.

Copy the properties and methods of the parent class to override the subclass prototype object

Prototype chain inheritance (New):

function fatherFn() {
  This. Some = 'this attribute of the parent class';
}
Fatherfn. Prototype. Fatherfnsome = 'property or method of parent prototype object';
// subclass
function sonFn() {
  This. Obkoro1 = 'this attribute of subclass';
}
//Core step: override the prototype object of subclass
Sonfn. Prototype = new fatherfn(); // assign an instance of fatherfn to the prototype of sonfn
sonFn.prototype.sonFnSome = '子类原型对象的属性或者方法' // subclass的属性/方法声明在后面,避免被覆盖
//Instantiation subclass
const sonFnInstance = new sonFn();
Console. Log ('instance of subclass: ', sonfninstance);

Prototype chain subclass instance

原型链子类实例

Prototype chain inherits and obtains the properties and methods of the parent class

  1. fatherFnProperties / methods declared through this are bound to thenewOn the new object created during.
  2. The prototype of the new object isfather.prototype, found by the properties of the prototype chainfather.prototypeProperties and methods for.

UnderstandnewWhat has been done:

New has appeared many times in this article. New is also an important part of JS foundation. Many knowledge points will involve new. If you don’t understand it, you should read it several times.

  1. Create a new object.
  2. The prototype of this new object(__proto__)Pointing to a functionprototypeObject.
  3. Execute the function, and this of the function will be bound to the newly created object.
  4. If the function does not return other objects (including arrays, functions, date objects, etc.), the new object will be returned automatically.
  5. The object returned is an instance of the constructor.

Construct call function to return other objects

Returning other objects will result in an instance that cannot get the constructor, which is easy to cause unexpected problems

We knowfatherFnOfthisandprototypeThe properties / methods ofnewNew objects created during have relationships

If other objects are returned in the parent class(newOther objects have no parentthisandprototype, so prototype chain inheritance fails

Let’s test and modify the parent class in the prototype chain inheritancefatherFn

function fatherFn() {
  This. Some = 'this attribute of the parent class';
  Console. Log ('object generated during 'new fatherfn', this)
  Return ['array object', 'function object', 'date object', 'regular object', 'etc.,' will not return the new object created during new ']
}

原型链继承返回其他对象,将导致原型链继承失败

PS: in this article, none of the constructed calling functions can return other functions, which will not be mentioned in the following.

Do not create prototype methods in the literal form of objects:

This way, it’s easy to inadvertently clear / overwrite the original properties / methods of the prototype object. It shouldn’t be used for the sake of a little simplicity.

Some people will create multiple properties and methods on prototype objects in the form of literal object:

sonFn.prototype = new fatherFn();
//The prototype of the subclass is cleared and reassigned, resulting in the failure of the previous line of code
sonFn.prototype = {
    Sofnsome: 'property of subclass prototype object',
    one: function() {},
    two: function() {},
    three: function() {}
}

There is also a common practice that results in properties of function prototype objectsconstructorLose:

function test() {}
test.prototype = {
    ...
}

Disadvantages of prototype chain inheritance

  1. Parent class usagethisDeclared properties are shared by all instances

    The reason is: instantiated parent class(sonFn.prototype = new fatherFn())Is the prototype of a one-time assignment to a subclass instance(sonFn.prototype)On, it passes the parent class through thethisThe declared property is also assigned to thesonFn.prototypeUp.

It’s worth mentioning that many blogs say that the properties of reference type are shared by all instances, usually using arrays as examples. In fact, arrays and other parent classes passthisThe declared property is only used to get the prototype of the subclass instance through the prototype chain lookup(sonFn.prototype)Value on.

  1. When creating a subclass instance, it is not flexible enough to pass parameters to the parent constructor.

The properties and methods of the parent class of this pattern are defined at the beginning, and parameters cannot be passed to the parent class, which is not flexible enough.

sonFn.prototype = new fatherFn()

Borrow constructor inheritance (call)

function fatherFn(...arr) {
  This. Some = 'this attribute of the parent class';
  This.params = arr // parameter of the parent class
}
Fatherfn. Prototype. Fatherfnsome = 'property or method of parent prototype object';
function sonFn(fatherParams, ...sonParams) {
  Fatherfn. Call (this,... Fatherparams); // core step: point this of fatherfn to this object of sonfn
  This. Obkoro1 = 'this attribute of subclass';
  This.sonparams = sonparams; // parameter of subclass
}
Sonfn. Prototype. Sonfnhome = 'property or method of subclass prototype object'
Let fatherparamsarr = ['parameter 1 of parent class',' parameter 2 of parent class']
Let sonparamsarr = ['parameter 1 of subclass',' parameter 2 of subclass']
Const sonfninstance = new sonfn (fatherparamsarr,... Sonparamsarr); // instantiate subclass
Console. Log ('borrow constructor subclass instance ', sonfninstance)

Subclass instances inherited by borrowing constructors

借用构造函数继承的子类实例

What does borrowing constructor inheritance do?

Declaration class, organization parameter, etc. are just auxiliary context code. The core is to use the constructorcallWhat has been done:

Once calledcall/applyThey execute the function immediately and change the function’sthispoint

fatherFn.call(this, ...fatherParams);
  1. Use in subclasscallCall the parent class,fatherFnWill be executed immediately and willfatherFnThis point of the functionsonFnOfthis
  2. Because the function executes, sofatherFnFunctions declared with this will beDeclare tosonFnOfthisobjectNext.
  3. Instantiate subclass, this will point tonewA new object created during, returns the new object.
  4. YesfatherFn.prototypeThere is no operation to inherit.

The properties of this object are: declared by subclass and parentthisProperty / method, its prototype is

PS: for more details about call / apply / bind, please check my blog: JS basic – how well do you understand call, apply and bind? [no regret series]

Advantages and disadvantages of borrowing constructor inheritance

Advantage:

  1. Parameters can be passed to the parent class
  2. Solve the problem of prototype chain inheritance: use of parent class attributethisDeclared properties will be shared by all instances of the problem.

Disadvantages:

  1. Only the parent class can be passedthisDeclared property / method, cannot inherit parent classprototypeProperty / method on.
  2. The parent method cannot be reused because it cannot inherit the parent’sprototype, so every subclass instantiation needs to execute the parent function and redeclare the parent classthisThe method defined in cannot be reused.

Combined inheritance (call + new)

principle: inheritance with prototype chain(newWillthisandprototypeThe declared property / method inherits from theprototypeUse the borrowing constructor to inherit the parent class through thethisDeclare properties and methods on properties of subclass instances.

function fatherFn(...arr) {
  This. Some = 'this attribute of the parent class';
  This.params = arr // parameter of the parent class
}
Fatherfn. Prototype. Fatherfnsome = 'property or method of parent prototype object';
function sonFn() {
  Fatherfn.call (this, 'borrow construction inheritance', 'second call'); // borrow construction inheritance: inherit the parent class to declare properties and methods to the properties of the child class instance through this
  This. Obkoro1 = 'this attribute of subclass';
}
Sonfn. Prototype = new fatherfn ('prototype chain inheritance ',' first call '); // prototype chain inheritance: inherit the properties / methods declared by' this' and 'prototype' to the 'prototype' of the subclass
Sonfn. Prototype. Sonfnhome = 'property or method of subclass prototype object'
const sonFnInstance = new sonFn();
Console. Log ('composite inheritance subclass instance ', sonfninstance)

Combining inherited subclass instances

组合继承的子类实例

You can see from the picturefatherFnadoptthisA copy of the declared property / method is copied on the property of the subclass instance and its prototype. The reason is also noted in the code:

  1. Prototype chain inheritance: parent class passingthisandprototypeThe declared property / method inherits from theprototypeUp.
  2. Borrowing construction inheritance: the parent class inherits to the properties of the child class instance through this declaration properties and methods.

Advantages and disadvantages of combinatorial inheritance

Advantage:

Complete inheritance (not unusable) solves the following problems:

  1. Parent passedthisThe problem of declaring property / method shared by subclass instance (the problem of prototype chain inheritance)
    Each instantiation of a subclass reinitializes the parent class through thethisDeclared property. The instance looks up the rule according to the prototype chain. Each time, it will
  2. Parent passedprototypeThe problem that declared properties / methods cannot inherit (the problem of borrowing constructors).

Disadvantages:

  1. Call parent function twice(new fatherFn()andfatherFn.call(this)), causing certain performance loss.
  2. The parent class passed because it was called twicethisDeclared properties / methods, generating two copies of the problem.
  3. Missing prototype chain context: properties / methods declared by subclass and parent through prototype exist on prototype of subclass

Archetypal inheritance(Object.create())

Inheritance object prototype – object. Create() implementation

Below isObject.create()Simulation implementation, usingObject.create()It can achieve the same effect, basically now it is usedObject.create()To do object prototype inheritance.

function cloneObject(obj){
  function F(){}
  F. Prototype = obj; // prototype that takes the inherited object as an empty function
  Return new f(); // returns the new object created during new. The prototype of this object is the inherited object. You can get the properties of the inherited object by searching the prototype chain
}

PS: above.Object.create()Remember how it works. Some companies may ask you to talk about how it works.

Example:

let oldObj = { p: 1 };
let newObj = cloneObject(oldObj)
oldObj.p = 2
console.log('oldObj newObj', oldObj, newObj)

原型式继承

Advantages and disadvantages of prototype inheritance:

Advantages: good compatibility, the simplest object inheritance.

Disadvantages:

  1. Because of old objects(oldObj)Is an instance object(newObj)Multiple instances share the properties of the inherited object, which may be tampered with.
  2. Unable to transmit reference

Parasitic inheritance (encapsulating the inheritance process)

Create aA function used only to encapsulate the inheritance process, which internally enhances the object in some way, and finally return the object.

function createAnother(original){
  Var clone = cloneobject (original); // inherit an object and return a new function
  //Do something enhances objects in some way
  Clone. Some = function() {}; // method
  Clone. Obkoro1 = 'encapsulate inheritance process'; // property
  Return clone; // returns this object
}

Use scene: enhance objects in a fixed way.

Parasitic combinatorial inheritance (call + parasitic encapsulation)

Principle of parasitic combinatorial inheritance:

  1. Use borrow constructor(callComeInherit the property / method declared by the parent class this
  2. Through the parasitic encapsulation function, the parent class prototype is set as the prototype of the child class prototypeProperties / methods declared by the prototype of the inheriting parent class
function fatherFn(...arr) {
  This. Some = 'this attribute of the parent class';
  This.params = arr // parameter of the parent class
}
Fatherfn. Prototype. Fatherfnsome = 'property or method of parent prototype object';
function sonFn() {
  Fatherfn.call (this, 'borrow construction inheritance'); // core 1 borrow construction inheritance: inherit the parent class to declare properties and methods to the properties of the child class instance through this
  This. Obkoro1 = 'this attribute of subclass';
}
//Core 2 parasitic inheritance: it encapsulates the process of son.prototype object prototype inheritance of parent.prototype, and enhances the incoming objects.
function inheritPrototype(son, father) {
  Const fatherfnprototype = object. Create (family. Prototype); // prototype inheritance: shallow copy of the family.prototype object family.prototype as the prototype of the new object
  Son. Prototype = fatherfnprototype; // set the parent.prototype to the prototype of son.prototype
  Son. Prototype. Constructor = son; // fix constructor point
}
inheritPrototype(sonFn, fatherFn)
Sonfn. Prototype. Sonfnhome = 'property or method of subclass prototype object'
const sonFnInstance = new sonFn();
Console. Log ('parasitic composite inheritance subclass instance ', sonfninstance)

Parasitic combinatorial inheritance subclass instance

寄生组合式继承子类实例

Parasitic combinatorial inheritance is the most mature inheritance method

Parasitic combinatorial inheritance is the most mature inheritance method, and it is also the most commonly used inheritance method now. It is also the inheritance scheme adopted by many JS libraries.

Compared with combinatorial inheritance, parasitic combinatorial inheritance has the following advantages:

  1. Call parent class only oncefatherFnConstructor.
  2. Avoid creating unnecessary and redundant properties on subclass prototype.
  3. Prototype is used to inherit the prototype of the parent class, keeping the prototype chain context unchanged.

    Only the properties / methods declared by the subclass through the prototype are distinct from those on the parent prototype.

ES6 extends inheritance:

The principle of ES6 inheritance is the same as that of parasitic combinatorial inheritance.

ES6 extendsCore code:

This code is compiled into Es5 online through Babel, which is used for subclass prototype to inherit the parent classprototypeProperty / method of.

//Parasitic inheritance encapsulates the inheritance process
function _inherits(son, father) {
  //Archetypal inheritance: the archetypal with the parent.prototype set to son.prototype is used to inherit the properties / methods of the parent.prototype
  son.prototype = Object.create(father && father.prototype);
  Son. Prototype. Constructor = son; // fix constructor point
  //Prototypes that set the parent as a child are used to inherit the static properties / methods of the parent (father. Some)
  if (father) {
    Object.setPrototypeOf
      ? Object.setPrototypeOf(son, father)
      : son.__proto__ = father;
  }
}

The other subclass is inherited by borrowing the constructor(call)To inherit the parent classthisThe declared property / method is the same as the parasitic combinatorial inheritance.

The difference between Es5 inheritance and ES6 inheritance:

This paragraph is excerpted from the introduction document of Ruan yifeng-es6

  • The inheritance of Es5 is essentiallyCreate the instance object of the subclass first, and then add the method of the parent class to this
  • The inheritance of ES6 isFirst create the instance object this of the parent class, and then modify this with the constructor of the child class

    Because a subclass does not have its own this object, it must first call the parent’s super () method.

Extension:

Why fix the construct point?

In the parasitic combinatorial inheritance, there is a piece of code that corrects the direction of constructor as follows. Many people don’t know its function and why to fix it.

Son. Prototype. Constructor = son; // fix constructor point

The function of construct

Definition of MDN:Returns theObjectReference to constructor

This returns a reference to the constructor of the instance object, for example:

let instance = new sonFn()
Instance. Constructor // sonfn function

constructApplication scenario:

When we have only instance objects without references to constructors

In some scenarios, we import and export instance objects through multiple rounds. We don’t know which function the instance is constructed from or the constructor that tracks the instance, which is more difficult.

At this time, you can use theconstructorProperty to get a reference to the constructor:

Let instance = new sonfn() // instantiate subclass
export instance;
//Multiple rounds of import + export cause trouble in sonfn tracking, or do not want to import sonfn into the file again
let  fn = instance.construct
//Do something: new fn() / FN. Prototype / FN. Length / FN. Arguments, etc

keepconstructConsistency of direction:

So every time you rewrite a function’s prototype, you should fix itconstructTo keep readingconstructConsistency of behavior.

Summary

Inheritance is also a front-end high-frequency interview question. Understanding the advantages and disadvantages of inheritance methods in this paper is helpful to a deeper understanding of JS inheritance mechanism. In addition to the combination inheritance and parasitic inheritance are composed of other methods, block understanding will have a deeper understanding of them.

It is suggested to read this article several times and build ahtmlFile try the example in the article, two-phase combination is better!

For those students who don’t understand prototype very well, let’s look at the relationship between JS basic functions, objects and prototypes, prototype chains

If you think my blog can help you, please give me a star!

Advanced accumulation of front-end, public address, GitHub, wx:OBkoro1, e-mail: [email protected]

Above September 22, 2019

Author: obkoro1

reference material:

JS advanced programming (Red Book) 6.3 inheritance

Eight inheritance schemes commonly used in JavaScript

Recommended Today

Nltk natural language processing library

Natural language processing, usually referred to as NLP, is a branch of artificial intelligence, dealing with the interaction between computers and people using natural language. The ultimate goal of NLP is to read, interpret, understand and understand human language in a valuable way. Most NLP technologies rely on machine learning to extract meaning from human […]