The introduction

Happy Mid-Autumn Festival everyone!! How are you doing? Don’t forget to review! Today we’re going to review archetypes, inheritance

Ⅰ prototype

— Learn the prototype

Before we get to prototypes, let’s take a quick look at constructors. Constructors in ECMAScript are used to create objects of a specific type. Let’s say we want to create a Person object:

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.sayName = function(){
        console.log(this.name); }}var p1 = new Peson("A".20."male");
var p2 = new Person("B".20."female");
Copy the code

The advantage of constructors is that the properties and methods created by constructors can be shared between instances, but there are drawbacks. If there is the same method between instances, the method will be created on each instance, which is obviously a waste of system resources. As shown in the following example, the methods called by instances P1 and P2 are obviously the same, but not equal, which means that the constructor creates two identical methods for both instances, which is obviously redundant.

p1.sayName();						//"A"
p2.sayName();						//"B"
console.log(p1.sayName == p2.sayName);			//false
Copy the code

And that’s where our real hero comes in — prototype mode.

The Person created using the constructor above, with the prototype:

function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}
Person.prototype.sayName = function(){
    console.log(this.name);
}
let p1 = new Person("Harry Potter".17."male");
let p2 = new Person("You know who".70."male");
p1.sayName();						//"Harry Potter"
p2.sayName();						//"You know who"
console.log(p1.sayName == p2.sayName);			//true
Copy the code

As you can see, the methods of the instance can now be shared.

— In-depth prototyping

Each function creates a Prototype property, which is an object containing properties and methods that should be shared by instances of a particular reference type. This object is actually a prototype of the object created by calling the constructor. The advantage of using a stereotype object is that the properties and methods defined on it can be shared by the object instance

Whenever a function is created, a Prototype attribute is created for that function according to specific rules. Constructors are no exception. In addition, the object instantiated by the constructor has a __proto__ attribute (two underscores) that, like the function’s prototype attribute, points to the constructor’s prototype object. The constructor property of the stereotype also refers back to the constructor

console.log(Person.prototype == p1.__proto__);		//true
console.log(Person.prototype.constructor == Person);	//true
Copy the code

Their relationship goes something like this

Once you understand the relationship of stereotype objects to instances and constructors, let’s look at the hierarchy of stereotypes to help you understand the shared behavior of stereotypes.

When a property is accessed through an object, a search is initiated by the name of the property. The search begins with the object instance itself. If a given name is found on this instance, the value of that name is returned. If the property is not found, the search follows the pointer into the prototype object, and when the property is found on the prototype object, the corresponding value is returned.

For example, when p1 calls the sayName() method, the js engine will first search p1 for the sayName() method, which does not exist, and then search the prototype object person.prototype. P2 calls the sayName() method in the same way, which is how stereotypes are used to share properties and methods across multiple object instances.

If we add a property or method that already exists in the stereotype to the instance, that property or method will mask the property or method of the same name in the stereotype. When the property or method is accessed, the property or method in the instance is returned first.

function Person(){}
Person.prototype.name = "Default Name";
let p1 = new Person();
let p2 = new Person();
p1.name = "New Name";
console.log(p1.name);							//"New Name"
console.log(p2.name);							//"Default Name"
Copy the code

In this example, we can clearly see the difference between the result returned by instance P1 and p2 calling attribute name.

Whenever you add a property to an object instance, that property will mask the property of the same name on the prototype object, meaning that it will not be modified, but will block access to it. Using the DELETE operator, you can remove this attribute completely on the instance, allowing the identifier resolution process to continue searching for the prototype object.

function Person(){}
Person.prototype.name = "Default Name";
let p1 = new Person();
p1.name = "New Name";
console.log(p1.name);							//"New Name"
delete p1.name;
console.log(p1.name);							//"Default Name"
Copy the code

– the prototype chain

As we said earlier, when you create a constructor, you create a prototype property for it. The prototype object has a property that refers back to the constructor and the instance has a __proto__ property that points to the prototype object. This looks like a triangulation, but what if the constructor’s prototype object is an instance of another type?

That means that the stereotype itself has an internal pointer to another stereotype, and the corresponding stereotype has a pointer to another constructor. This creates a chain of stereotypes between the instance and the stereotype. This is the basic idea of a prototype chain.

When reading a property on an instance, the property is first searched on the instance. If not, the prototype of the search instance is inherited. After inheritance is implemented through the prototype chain, the search can inherit the archetype that searches for the archetype up. The search for properties and methods goes all the way to the end of the prototype chain

The default prototype of any function is an Object instance. When we continue to search for properties and methods through any function instance, we will eventually find the Object prototype. Further access will return null, and that is the end of the story. In the case of the Person constructor, whose prototype is an instance of Object, the following two lines of code can be verified

console.log(Person.prototype.__proto__ == Object.prototype);	//true
console.log(Person.__proto__.__proto__ == Object.prototype);	//true
Copy the code

This process is shown in figure

— Prototyping problems

The problem arises when there are attributes in the stereotype that reference values

function COlor(){};
Color.prototype.colors=['red'.'green'.'blue'];
let c1 = new Color();
let c2 = new Color();
console.log(c1.colors);							//['red','green','blue']
console.log(c2.colors);							//['red','green','blue']
c1.colors.push('black');
console.log(c1.colors);							//['red','green','blue','black']
console.log(c2.colors);							//['red','green','blue','black']
Copy the code

Obviously, instances C1 and C2 share the same array property, and when one modifies the property, the other changes as well. If this is intended to share arrays across multiple instances, that’s fine. But in general, different instances should have their own copies of properties. In general, attributes are defined in constructors, so this is not a big deal.

Ⅱ inheritance

— Prototype chain inheritance

So, once we’ve understood the prototype and the prototype chain, we can talk about inheritance, first of all, prototype chain inheritance. Let’s use examples to help us understand.

function A(){
    this.messageA = "message from A"
}
A.prototype.getA = function(){
	return this.messageA;
}
function B(){
    this.messageB = "message from B"
}
B.prototype = new A();
B.prototype.getB = function(){
	return this.messageB;
}
let C = new B();
console.log(C.getA());							//"message from A"
Copy the code

We know that when we read a property on an instance, we first search for that property on the instance. If not, the prototype of the search instance is inherited. The prototype object of constructor B in the above example becomes an instance of constructor A. As an example, the prototype object of B has methods that construct the prototype object of A. This example C implements inheritance.

Note also that since the prototypeobject of the subclass constructor becomes an instance of the parent constructor directly, the prototypeobject of the subclass constructor loses its constructor reference, which can be recovered using the following method

Object.defineProperty(B.prototype,"constructor", {enumerable:false.value:B
})
// Since modifying the constructor point of a prototype Object with a '=' assignment causes the property to become enumerable, we use Object.defineProperty to specify Enumerable as false
Copy the code

Problems with prototype chain inheritance:

  1. The reference property problem, we talked about the problem of adding reference value properties to stereotype objects, and although we usually put attributes in constructors, this problem recurred in stereotype inheritance, as shown in the following example

    function A(){
        this.arrs=[1.2.3]};function B(){}
    B.prototype = new A();
    let c1 = new B();
    let c2 = new B();
    console.log(c1.arrs);						/ / [1, 2, 3]
    console.log(c2.arrs);						/ / [1, 2, 3]
    c1.arrs.push(4);
    console.log(c1.arrs);						/ / [1, 2, 3, 4]
    console.log(c2.arrs);						/ / [1, 2, 3, 4]
    Copy the code
  2. A subclass cannot take arguments to its parent constructor when instantiated

— Embezzle the constructor

This method of inheritance is to solve the two problems of the above prototype inheritance

function A(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.arrs=[1.2.3]};function B(name,age,sex){
    A.apply(this.arguments)}let C1 = new B('C1'.18.'male');
let C2 = new B('C2'.18.'female');
console.log(C1.name,C1.age,C1.sex,C1.arrs);		/ / 'C1' and 'male' [1, 2, 3]
console.log(C2.name,C2.age,C2.sex,C2.arrs);		/ / 'C2' and 'getting' [1, 2, 3]
C1.arrs.push(4);
console.log(C1.arrs);					/ / [1, 2, 3, 4]
console.log(C2.arrs);					/ / [1, 2, 3]
Copy the code

In the above example, constructor B uses the apply or call methods internally, so that constructor B can pass arguments to its parent class when instantiated. We can also see that the reference value attribute ARRS for instance C1 and C2 is one for each of them, and does not affect each other.

Problem with embezzling constructors

  1. Because the prototype object of the subclass constructor has nothing to do with the parent class, the subclass instance cannot call the properties or methods of the parent class’s prototype object, but can only inherit the properties or methods of the parent class constructor.
  2. Attributes or methods in the parent constructor will be created in one copy for each subclass instance, which is bloated

— Combinatorial inheritance

Composite inheritance combines stereotype chains and stolen constructors, bringing together the best of both. You can either define methods on prototypes for reuse or have each instance have its own attributes.

function A(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.arrs = [1.2.3]}; A.prototype.sayName =function(){
    return this.name
}
function B(name,age,sex){
    A.apply(this.arguments)
}
B.prototype = new A();
Object.defineProperty(B.prototype,"constructor", {enumerable:false.value:B
})
let C1 = new B('C1'.18.'male');
let C2 = new B('C2'.18.'female');
console.log(C1.sayName());					//"C1"
C1.arrs.push(4);
console.log(C1.age,C1.sex,C1.arrs);				/ / 18, 'male', [1, 2, 3, 4]
console.log(C2.sayName());					//"C2"
console.log(C2.age,C2.sex,C2.arrs);				/ / 18, 'female' and [1, 2, 3]
Copy the code

In this example, constructor B inherits A’s stereotype method using stereotype chain inheritance. It then inherits instance properties by stealing constructors. Each instance ends up with its own attributes name, age, sex, and the reference-value attribute arrs, as well as the shared method sayName().

Problems with composite inheritance

The superclass constructor is called twice, once when the child constructor uses apply/ Call and once when the parent class instance is assigned to the subclass’s prototype object when the subclass uses stereotype inheritance

conclusion

That’s all for this article, and the next one will review promises