1. Learn about its history and origin:

The design idea of Javascript inheritance mechanism

Thank @Ruan Yifeng teacher, borrow ruan general article a use as follows:

I’ve always had a hard time understanding the inheritance mechanism of the Javascript language.

It doesn’t have a “subclass” or a “superclass” or a “class” or an “instance” distinction. It relies on a curious “prototype chain” pattern to implement inheritance.

I spent a lot of time studying this part and taking a lot of notes. But they are forced memories that cannot be fundamentally understood. Now, I’ll try to explain the design idea in my own words. Explain the Prototype object once and for all. It’s not that complicated at all. The truth is very simple.

First, start from ancient times

To understand the design of Javascript, you have to start with its birth.

In 1994, Netscape released version 0.9 of its Navigator browser. It was the first full-fledged web browser in history, and it was a hit. However, this version of the browser can only be used for browsing and does not have the ability to interact with visitors. For example, if a web page asks for a “user name,” the browser can’t tell if the visitor actually filled it in, leaving it to the server. If not, the server returns an error and asks the user to fill it out again, which is a waste of time and server resources. So Netscape desperately needed a web scripting language that would allow the browser to interact with the web. Engineer Brendan Eich is responsible for developing the new language. He said it didn’t need to be complicated, but that the language could do simple things, such as determine whether a user had filled out a form.

In 1994, object-oriented programming was in its heyday, C++ was the most popular language, and the 1.0 version of the Java language was coming the following year, and Sun was on a roll.

Brendan Eich was definitely affected. All data types in Javascript are objects, much like in Java. But he then faced a dilemma: should he design an inheritance mechanism?

Brendan Eich’s choice

If it were really a simple scripting language, there would be no need for “inheritance”. However, Javascript is full of objects, and there must be a mechanism to link them all together. So Brendan Eich ended up designing inheritance.

However, he is not going to introduce the concept of “classes,” which would make Javascript a full object-oriented programming language, which seems a bit too formal and makes getting started difficult for beginners.

He took into account that both the C++ and Java languages use the new command to generate instances.

C++ is written as:

ClassName *object = new ClassName(param);

Java is written as:

Foo foo = new Foo();

Therefore, he introduced the new command into Javascript to generate an instance object from a prototype object. However, Javascript doesn’t have “classes”, so how do you represent prototype objects?

Both C++ and Java use the new command to call the constructor of the class. Instead of a class, the new command is followed by a constructor in Javascript.

For example, there is now a constructor called DOG that represents the prototype of a DOG object.

function DOG(name){

this.name = name;

}

Using new on this constructor generates an instance of the dog object.

Var dogA = new DOG(‘ DOG ‘);

alert(dogA.name); / / heavy hair

Notice the this keyword in the constructor, which represents the newly created instance object.

Disadvantages of the new operator

Using constructors to generate instance objects has the disadvantage of not sharing properties and methods.

For example, in the constructor of the DOG object, set the common attribute of an instance object, species.

function DOG(name){

this.name = name;

This. Species = ‘species ‘;

}

Then, two instance objects are generated:

Var dogA = new DOG(‘ DOG ‘);

Var dogB = new DOG(‘ erao ‘);

The species property of these two objects is independent, and modifying one does not affect the other.

Doga. species = ‘feline ‘;

alert(dogB.species); // Display “canine”, not affected by dogA

Each instance object has its own copy of properties and methods. This not only fails to share data, but is also a huge waste of resources.

Prototype attribute introduction

With this in mind, Brendan Eich decided to set a Prototype property for the constructor.

This property contains an object (the “Prototype object”) in which all instance objects share properties and methods. Properties and methods that do not need to be shared are stored in constructors.

The instance object, once created, automatically references the properties and methods of the Prototype object. That is, the properties and methods of an instance object are divided into two types, one is local and the other is referenced.

Using the DOG constructor as an example, now overwritten with the Prototype property:

  function DOG(name){this.name = name; } t prototype = {species : 'dog'};var dogA = new DOG('heavy');var dogB = new DOG('二毛'); alert(dogA.species);/ / caninealert(dogB.species);/ / canine
Copy the code

The Species property is now in the Prototype object and is shared between the two instance objects. Whenever you modify the Prototype object, both instances are affected.

Dog.prototype. species = ‘cat ‘;

alert(dogA.species); / / the cat

alert(dogB.species); / / the cat

2. More on prototypes and prototype chains

1. Introduce the function and working principle of prototype chain.

2. content everything is an object; Objects are created by functions; Prototype and prototype chain. 3 Everything is object this sentence means that most of the data we contact in JS are objects, with the characteristics of objects. Let’s expand on how to understand “everything is an object”.

3.1 Functions are Objects Let’s take a look at our daily definition of functions:

var f = function () {}
// Call the function
f(); 
// Add attributes to the function
f.abc = 100; 
// Add attributes to the function
f.f1 = function(){}; 
consoleDir (f), I intentionally added two attributes to f ABC,f1, the code does not have any syntax error. The output is as follows (using Chrome)Copy the code

3.2 Constructors are Objects Take a look at the built-in constructors used in everyday life

// Define an array: constructor
var arr = new Array(a);// Define an array, literal
var arr = []
// Output constructor
console.dir(Array)
Copy the code

Objects are created by functions. In JS, when we talk about a function, we are not really sure what we are talking about, because functions have two roles.

• Basic features. As an encapsulation function, a tool to refine code. • Class functionality. A function can be used as a constructor by prefixing it with new.

Let’s look at some common code for creating objects

var arr = new Array();

var fn = new Function();

var obj = new Object();

In each of the three lines above, we create objects as new constructors (), and constructors (Array,Function,Object, etc.) are functions. Note that in real development it is more likely to be created literally, but this is just a demonstration.

More generally, let’s look at custom functions when used by constructors:

function F () {}

Var rs = F();

Var f1 = new F(); Well, if you accept the proposition that everything is an object and objects are created by functions, then we are about to embark on a long search for the constructor that created this object.

4.1 Finding constructors How do I find the constructor that created obJ? The formula is as follows:

Proto takes the __proto__ attribute of an object. We know that an object is an unordered collection of sets, so one of these properties is called __proto__. Consider the object’s.__proto__ value as an object again and look for its constructor property.

Take arrays, for example. (In fact, we already know that arrays are created by the Array constructor, so let’s pretend we don’t know.)

Use this formula to find its constructor :(the following code executes in chrome)

Log.console ([].proto.constructor)// ƒ Array() {[native code]}

[].proto.constructor === Array

In the same way, we can continue to find string, object, function, a tag constructor. The results are as follows:

var str =”” console.log(str.proto.constructor === String); // true

var obj = {} console.log(obj.proto.constructor === Object); // true

var f = function () {} console.log(f.proto.constructor === Object); // true

Suppose you have an “A” tag on your page whose ID is “A”

Var a = document.getelementById (‘a’) a.constructor === HTMLAnchorElement

As mentioned earlier, an object is a collection of properties, and a function is also an object. So, every function has a lot of properties, but one of them is called the Prototype property, and the value of that property is also an object. It’s a very important property that we call a prototype.

Let’s see what the prototype of the function looks like:

(1) Custom functions

var f = function() {}

console.dir(f)

(2) the Array constructor

console.dir(Array)

Since prototype’s value is also an object, prototype is also called a prototype object.

5.2 Implicit prototype Proto

Objects are collections of properties, and each object may have a variable number of properties, but each object has a property called Proto whose value is also an object. This property, Proto, is called an implicit stereotype. (Note how it is written: before and after two underscores)

Note: Since a function is both a function and an object, for a function it has both a prototype and a __proto__ attribute.

5.3 The relationship between prototype and implicit prototype is directly concluded:

Prototype: the implicit prototype of an object equals the prototype of the constructor that created the object.

Expressed in the formula:

If: object = new constructor ()

So: object. Proto === constructor. Prototype

5.4 Verify this relationship

An array of

console.log([].proto === Array.prototype); // true

function

var f = function() console.log(f.proto === Function.prototype); //true

Ordinary objects

console.log({}.proto === Object.prototype); // true The object created by the custom constructor

function f(){}

var f1 = new f()

console.log( f1.proto === f.prototype )

When we access a property of an object, we have to follow a path to find that property, and that path is the prototype chain.

Specifically, take the access to the property P of obj as an example

Console.info (obj.p) The steps to find attribute P are as follows:

(1) First look in its own attributes, if found, then return; If you can’t find it, go into its implicit prototype.

(2) Since the implicit prototype of an object is also an object (which in turn is a collection of properties), continue to look in the own-ownership of obj.__proto__. If found, return; if not found, continue.

(3) Continue looking in the implicit prototype of the object obj.__proto__. __proto__ : obj. Proto.__proto__ : obj. Proto.

(4) Stop until __proto__ is null.

6.1 Example: The Search succeeds

Var arr = [1, 2]; // This is an object.

arr.toString()

Think of arr as an object, and arr. ToString is going to find the toString property. (1) The first step is to find the property of its own, not found (2) the second step is to enter the object arr. Proto to find. If found, return to use.

In other words,

arr.toString === arr.proto.toString

6.2 Find examples of failures

var arr = []; // This is an object.

Arr. ABC treats arr as an object, and arr. ToString is going to find the toString property.

(1) The first step, in its own attributes to find, did not find

(2) Step 2, go to the arr.__proto__ object. Not found.

(3) Step 3: Go to arr.proto.__proto__. Not found.

Proto. Proto. Proto. Proto.

Application of prototype chain

Principle: After you add a property (or method) to a constructor’s prototype object, all objects created by the constructor can use the property (method).

7.1 Example 1: Adding attributes to the built-in constructor

For example, add an a1() method to the prototype object added to the Array Array constructor. All array objects will have this method.

Array.prototype.a1 = function(){ console.log(‘a’) }

var arr1 = new Array();

var arr2 = []; // This is equivalent to using the constructor

arr1.a1(); / / output a

arr2.a1(); / / output a

Take arr1.a1 as an example, according to the search process of prototype chain, first find in its own attributes, and then go further to find in arr.__proto__.

Note that:

arr.proto === Array.prototype

In array. prototype we already added the a1 attribute, so it will be found in arr.__proto__. As a result, arr.a1() executes successfully.

Similarly, the search process of ARR2. A1 can be analyzed.

In fact, since a1 and a2 are objects created by the constructor Array, the following formula holds

arr1.proto === arr2.proto === Array.prototype

7.2 Example 2: Adding attributes to a custom constructor

The following code is a classic example of using a function as a constructor to simulate the concept of a class facing an object.

functioin F() {}

F.prototype.func = function() {}

var f1 = new F();

f1.func()

So why can f1 access func? If you can answer this question, you have learned the prototype chain.

• If object = new constructor (), then object. Proto === constructor. Prototype

• The role of the prototype chain is to determine the attribute value of the object;

• When determining an object’s attribute value, we look up the prototype chain. Proto. Proto. If not, proto. Proto. Proto is null. The search path for this process is the prototype chain.

Summary: Prototype prototype chain; Functions have two values: 1. implement concrete 2. as objects; Functions are functions and objects that have both prototype and __proto__

// The relationship between stereotype and implicit stereotype

// Each constructor has a prototype property, from which all objects created by the constructor inherit.

Ptoto == constructor. Prototype

// The implicit stereotype of an object is equal to the stereotype of the constructor that created the object

//
function Func() {
  this.name = 11;
} // Both prototype and __proto__
// Every object has __proto__

Func.prototype.func = function() {
  // console.log('222');
};

var nfunc = new Func();
nfunc.func();
nfunc.name;

console.log(nfunc.__proto__ == Func.prototype);

//nfunc.__proto__ == Func.prototype
//Func.prototype__proto__ == Object.prototype;
//Object.prototype.__proto__ == null

function Munc(name, sex) {
  this.name = name;
  this.sex = sex;
}

Munc.prototype.hellword = function() {
  console.log(this.name, this.sex);
};

var m_func = new Munc("111"."man");
m_func.hellword();
Copy the code

Deepen knowledge points to memorize a classic topic to achieve a new function

///A. Implement A new 1. Create an empty object 2 object's __proto__ == caller's prototype 3. Change the point to Apply
// Note: methods on functions are mounted on __proto__
// The first step is empty object
// The second step is to pass the empty object's __proto__ == prototype through the prototype chain so that the empty object can call the method passed in
// The third step is to change the point of this via apply or call so that the empty object can access the property value of the passed object
function _new() {
  var _object = {};

  let constructor = [].shift.apply(arguments); // Arguments are array-like objects that need to be converted or array.prototype
  _object.__proto__ = constructor.prototype;

  let resback = constructor.apply(_object, arguments);

  return resback;
}
//B The second, more succinct method uses object.create to create a new Object
function _new() {
  var _object = {};
  let nModel = Array.prototype.shift.call(arguments);
  _object = Object.create(nModel);
  let res = nModel.apply(_object, arguments);
  return res;
}
Copy the code

3. Look at inheritance after learning about stereotypes and stereotype chains

Thanks for the article of @rowboat big guyThere are six common ways to inherit JavaScript

In general, there are two types of javaScript inheritance from ES5:

A kind of archetypal inheritance; A borrowed constructor changes the this pointer to inherit

1. Prototype chain inheritance

The key here is that the prototype of a subtype is an instance object of the parent type.

       / / parent types
       function Person(name, age) {
           this.name = name,
           this.age = age,
           this.play = [1.2.3]
           this.setName = function () { }
       }
       Person.prototype.setAge = function () {}/ / subtypes
       function Student(price) {
           this.price = price
           this.setScore = function () { }
       }
       Student.prototype = new Person() // The prototype of a subtype is an instance object of the parent type
       var s1 = new Student(15000)
       var s2 = new Student(14000)
       console.log(s1,s2)
Copy the code

But the essence of this approach is to point the prototype of the subclass to the instance of the parent class, so that the instance of the subclass can access the student. prototype instance of Person through __proto__, so that the private method of the parent class can be accessed. Then __proto__ points to the parent’s prototype to get the method on the parent’s prototype. So we treat the private and public methods and properties of the parent class as the public properties of the child class;

Subclass inherits properties and methods of the parent is the parent class of private property and public methods as their own public attributes and methods, we all know that at the time of operation basic data types operating value, at the time of operation reference data type operation is address, if there is a reference type in the private property of the parent’s property, it was a subclass inherits as public property, So when subclass 1 operates on this property, it affects subclass 2.

The play property in S1 changes, and the play property in S2 changes as well.

It is also important to note that when adding a new method to a subclass or overwriting a method from a parent class, it must be placed after the statement that replaces the prototype

       function Person(name, age) {
           this.name = name,
           this.age = age
       }
       Person.prototype.setAge = function () {
           console.log("111")}function Student(price) {
           this.price = price
           this.setScore = function () {}}/ / Student. Prototype. SayHello = function () {} / / here to write a subclass of prototype method and attribute is invalid,
      // Because it changes the orientation of the prototype, it should be placed after the reassignment
       Student.prototype = new Person()
       Student.prototype.sayHello = function () {}var s1 = new Student(15000)
       console.log(s1)

Copy the code

Features:

Superclass new prototype methods/prototype attributes, subclasses can access simple, easy to implement

Disadvantages:

New attributes and methods for a subclass must be executed after student.prototype = new Person(), and cannot be placed in the constructor

To sum up: in a word, if you don’t have a function, go to prototype. If you want to publish it, put it on prototype for others to inherit

Constructor inheritance

The key to this approach is that the generic call() in the subtype constructor calls the parent type constructor

<script type="text/javascript">
  function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setName = function () {}
  }
  Person.prototype.setAge = function () {}
  function Student(name, age, price) {
    Person.call(this, name, age)  This.Person(name, age)
    /*this.name = name this.age = age*/
    this.price = price
  }
  var s1 = new Student('Tom'.20.15000)

Copy the code

This is only partial inheritance. If the parent class has methods and attributes, the subclass cannot get those methods and attributes. Console. log(s1.setage ())//Uncaught TypeError: s1.setage is not a function

[Fixed] When creating a subclass instance, you can pass parameters to the parent class. You can implement multiple inheritance (call multiple parent objects).

Disadvantages:

An instance is not an instance of a parent class, only an instance of a subclass

Only instance properties and methods of the parent class can be inherited, not stereotype properties and methods

Function reuse is not possible, each subclass has a copy of the parent class instance function, affecting performance

The combination of stereotype inheritance and constructor is a bit of a refinement of inheritance

The key to this approach is to inherit the attributes of the parent class and retain the advantages of passing arguments by calling the parent class constructor, and then reuse functions by using the parent class instance as the prototype of the subclass.

        function Person(name, age) {
            this.name = name,
            this.age = age,
            this.setAge = function () { }
        }
        Person.prototype.setAge = function () {
            console.log("111")}function Student(name, age, price) {
            Person.call(this,name,age)
            this.price = price
            this.setScore = function () { }
        }
        Student.prototype = new Person()
        Student.prototype.constructor = Student// Composite inheritance also needs to be fixed to the constructor point
        Student.prototype.sayHello = function () {}var s1 = new Student('Tom'.20.15000)
        var s2 = new Student('Jack'.22.14000)
        console.log(s1)
        console.log(s1.constructor) //Student
        console.log(p1.constructor) //Person

Copy the code

This approach combines the advantages of stereotype chain inheritance and constructors and is the most commonly used inheritance pattern in JavaScript. But there is also a downside is that under no circumstances, will be called two constructors: one is to create a subtype prototype, another is in the interior of the subtype constructor, subtype will eventually contain all parent type object instance attributes, but we have to rewrite these properties when invoking the subclass constructor.

Advantages:

1. You can inherit instance properties/methods as well as stereotype properties/methods

2. There is no reference attribute sharing problem

3. Can be involved

4. Function reuse

Disadvantages:

The parent constructor is called twice, generating two instances

Combinatorial inheritance optimization 1

In this way, the parent class prototype and the subclass prototype point to the same object. The subclass can inherit the public method of the parent class as its own public method, and will not initialize the method/attribute twice, avoiding the disadvantages of combinatorial inheritance.

       function Person(name, age) {
            this.name = name,
                this.age = age,
                this.setAge = function () { }
        }
        Person.prototype.setAge = function () {
            console.log("111")}function Student(name, age, price) {
            Person.call(this, name, age)
            this.price = price
            this.setScore = function () { }
        }
        Student.prototype = Person.prototype
        Student.prototype.sayHello = function () {}var s1 = new Student('Tom'.20.15000)
        console.log(s1)


Copy the code

But there’s no way to tell if the object is subclass or superclass instantiated

Advantages:

Methods/attributes are not initialized twice, avoiding the disadvantage of composite inheritance

Disadvantages:

There is no way to tell whether the instance was created by a subclass or a superclass. The subclass and the superclass have the same constructor pointing to the same object.

Combinatorial inheritance optimization 2

Var B = object.create (A) creates an Object based on an existing Object. B inherits all of A’s properties and methods.

       function Person(name, age) {
            this.name = name,
            this.age = age
        }
        Person.prototype.setAge = function () {
            console.log("111")}function Student(name, age, price) {
            Person.call(this, name, age)
            this.price = price
            this.setScore = function () {}
        }
        Student.prototype = Object.create(Person.prototype)// Core code
        Student.prototype.constructor = Student// Core code
        var s1 = new Student('Tom'.20.15000)
        console.log(s1 instanceof Student, s1 instanceof Person) // true true
        console.log(s1.constructor) //Student
        console.log(s1)


Copy the code

Similarly, Student inherits all the attributes and methods of the Person prototype object. At present, the most perfect inheritance method!

4. Finally: Class inheritance in ES6

The class keyword is introduced in ES6. The class extends extends extends, and static methods of a class are defined using the static keyword. This is much cleaner and more convenient than ES5’s inheritance by modifying the stereotype chain.

ES5 inheritance essentially creates an instance object of the subclass, this, and then adds the Parent class’s methods to this (parent.apply (this)). ES6 has a completely different inheritance mechanism, essentially adding the properties and methods of the superclass instance object to this (so the super method must be called first), and then modifying this with the constructor of the subclass.

Note that the class keyword is just syntactic sugar for the stereotype, and JavaScript inheritance is still implemented based on the stereotype.

       class Person {
            // Call the class constructor
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            // Define general methods
            showName() {
                console.log("Call method of parent class")
                console.log(this.name, this.age); }}let p1 = new  Person('kobe'.39)
        console.log(p1)
        // Define a subclass
        class Student extends Person {
            constructor(name, age, salary) {
                super(name, age)// Call the parent constructor with super
                this.salary = salary
            }
            showName() {// Define methods in the subclass itself
                console.log("Calling a subclass's method")
                console.log(this.name, this.age, this.salary); }}let s1 = new Student('wade'.38.1000000000)
        console.log(s1)
        s1.showName()


Copy the code

Advantages: Simple syntax and easy operation

Disadvantages:

Not all browsers support the class keyword