Object oriented and object based

Almost every developer has some experience developing an object-oriented language such as C++, C#, Java. In traditional object-oriented languages, there are two very important concepts — classes and instances. Classes define the common behaviors and methods of some things; An instance is a concrete implementation of a class. We also know that object-oriented programming has three important concepts — encapsulation, inheritance, and polymorphism. But in the world of Javascript, all of these features don’t seem to exist. Because Javascript itself is not an object-oriented language, it’s an object-based language. Everything in Javascript is an object, including strings, arrays, dates, and even functions. Here’s an interesting example:

// Define a functionfunction add(a,b){
	add.invokeTimes++;
	returna+b; } // Since the function itself is also an object, here we define an attribute for add, which records the number of times the function is called. Add (1, 1); Add (2, 2); console.log(add.invokeTimes); / / 2Copy the code

Emulate classes and inheritance in Javascript

In an object-oriented language, we use classes to create a custom object. Whereas everything in Javascript is an object, how do you create custom objects? Here we introduce a new concept called prototype. We can simply think of Prototype as a template and create custom objects that are copies of a prototye (actually not copies but links that are invisible). It feels like a copy). An example of creating a custom object using Prototype:

// constructorfunctionPerson(name,gender){ this.name = name; this.gender = gender; } / / definition of Person prototype, the prototype of the properties can be custom object reference Person. The prototype = {getName:function() {return this.name;
	},
	getGender:function() {
		returnthis.gender; }}Copy the code

Here we call the function Person the constructor, which is the function that creates a custom object. As you can see, Javascript simulates the functionality of classes through structural functions and prototypes. Create a custom object (instantiated class) :

var Person1 = new Person("Zhang"."Male"); console.log(Person1.getName()); Var Person2 = new Person()"Nana"."Female"); console.log(Person2.getName()); / / nanaCopy the code

When var Person1 = new Person(” Joe “,” Joe “) is executed, it does the following:

Create a blank Object(New Object()). Copy the attributes (key-value pairs) from Person.prototype into the empty object (as we mentioned earlier, the internal implementation is not a copy but a hidden link). Pass the object to the constructor via the this keyword and execute the constructor. Assign this object to the variable Person1.

To prove that the Prototype template is not copied into an instantiated object, but rather linked, see the following example:

function Person(name,gender){
	this.name = name;
	this.gender= gender;
}
Person.prototype.age = 20;
var Person1 = new Person('nana'.'woman'); console.log(Person1.age); // Override the prototype age attribute person1.age = 25; console.log(Person1.age); //25 delete Person1.age; // Get console.log(person1.age) from prototype after removing the instance attribute age; / / 20Copy the code

Javascript inheritance in several ways

To illustrate several ways of Javascript inheritance, we first agree on a common language in advance:

/ / agreementfunction Fun(){var val = 1; Var arr = [1]; // Private reference propertiesfunction fun() {} // Private function (reference attribute) // instance attribute this.val = 1; // Public base this.arr = [1]; // The public reference attribute this.fun =function() {}; // fun.prototype. val = 1; Fun.prototype.arr = [1]; // Prototype reference property fun.prototype.fun =function() {}; // Prototype function (reference properties)Copy the code

A simple prototype chain to achieve inheritance

This is the easiest way to implement inheritance. If the “cat” prototype object points to an Animal example, then all instances of “cat” will inherit Animal.

The specific implementation

function Animal(){
	this.species = "Animal";
	this.classes = ['Vertebrate'.'Reptile'];
}
functionCat(name,color){ this.name = name; this.color = color; Cat. Prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat("Heavy hair"."Yellow");
var cat2 = new Cat("二毛"."White");
cat1.classes.push('Mammals');
cat1.species = 'Mammals'; console.log(cat1.species); // Mammal console.log(cat2.species); The console / / animals. The log (cat1. Classes). / / /"Vertebrates"."Reptile"."Mammals"] console.log(cat2.classes); / / /"Vertebrates"."Reptile"."Mammals"]
Copy the code

We point the Cat prototype object to an Animal example.

Cat.prototype = new Animal();
Copy the code

This is equivalent to completely deleting the original value of the Prototype object and assigning a new value.

Cat.prototype.constructor = Cat;
Copy the code

Any Prototype object has a constructor property pointing to its constructor. Cat. Prototype = new Animal(); This line, the prototype. The constructor is point to the Cat. After this line, the prototype. The constructor to Animal.

console.log(Cat.prototype.constructor == Animal); //true
Copy the code

More importantly, each instance also has a constructor property, which by default calls the Constructor property of the Prototype object.

console.log(cat1.constructor = Cat.prototype.constructor); //true
Copy the code

So, after running “cat.prototype = new Animal();” After this line, cat1.constructor also points to Animal!

console.log(cat1.constructor == Animal); //true
Copy the code

This obviously leads to a disorder in the inheritance chain (cat1 is clearly generated by the Cat constructor), so we have to correct this manually by changing the constructor value of the cat. prototype object to Cat. This is an important point to follow when programming. If I replace the prototype object,

o.prototype = {};
Copy the code

The next logical step, then, is to add the Contructor property to the new Prototype object and point it back to the original constructor.

o.prototype.constructor = o;
Copy the code

Existing problems

Cat2.classes also changes after modifying cat1.classes, because the referenced properties from the prototype object are shared by all instances. Cat1.classes.push (‘ mammal ‘); First, I searched all the instance attributes of CAT1 (in this case, there are no instance attributes). When I didn’t find any, I started to look up the prototype chain and got the prototype object of CAT1. When I searched, I found classes attributes. ‘Mammals’ was inserted at the end of classes, so classes changed.

2. Cannot pass arguments to the parent class constructor when creating a subclass instance.

Borrow constructors and call or apply methods

Simple prototype chain is really simple enough, but there were two fatal defects that could not be used, so Jsers at the end of the last century tried to fix these two defects, and then the way of borrowing constructor appeared.

The specific implementation

function Animal(species){
	this.species = species;
	this.classes = ['Vertebrate'.'Reptile'];
}
functionCat(name,color,species){ Animal.call(this,species); // core this.name = name; this.color = color; } var cat1 = new Cat("Heavy hair"."Yellow".'animals');
var cat2 = new Cat("二毛"."White".'Mammals');

cat1.classes.push('Mammals'); console.log(cat1.species); The console / / animals. The log (cat2. Species); // Mammal console.log(cat1.classes); / / /"Vertebrates"."Reptile"."Mammals"] console.log(cat2.classes); / / /"Vertebrates"."Reptile"]
Copy the code

The core

Augmenting a subclass instance with a parent class constructor is equivalent to adding a copy of the parent class’s instance properties to the subclass instance (without using the stereotype at all).

The advantages and disadvantages

Advantages: 1. Solve the problem that subclass instances share parent class reference attributes; 2. When creating a subclass instance, you can pass arguments to the superclass constructor. Disadvantages: function reuse is not possible, each subclass instance holds a new fun function, too many will affect performance, memory explosion.

3. Combinatorial Inheritance (most Commonly used)

At present, we still have problems borrowing constructors (can not achieve function reuse), it does not matter, and then fix, so there is combinatorial inheritance.

The specific implementation

functionAnimal(species){this.species = species; this.classes = ['Vertebrate'.'Reptile']; } // Declare the function animal.prototype. eat = herefunction(){
	console.log('Animals must eat for energy');
}
Animal.prototype.run = function(){
	console.log('The animal is running');
}
functionCat(name,color,species){ Animal.call(this,species); // core this.name = name; this.color = color; } Cat.prototype = new Animal(); var cat1 = new Cat("Heavy hair"."Yellow".'animals');
var cat2 = new Cat("二毛"."White".'Mammals');

cat1.classes.push('Mammals'); console.log(cat1.species); The console / / animals. The log (cat2. Species); // Mammal console.log(cat1.classes); / / /"Vertebrates"."Reptile"."Mammals"] console.log(cat2.classes); / / /"Vertebrates"."Reptile"] console.log(cat1.eat === cat2.eat); //true
Copy the code

The specific implementation

Instance functions are placed on prototype objects to achieve function reuse. Call (this,species) inherits the base and reference attributes of the parent class and retains the advantage of passing arguments. Cat.prototype = new Animal(), inherit the parent class function, function reuse.

The advantages and disadvantages

Advantages: 1. No sharing of reference attributes 2. Passable arguments 3. Another memory waste, but much improved.

4, Direct inheritance prototype(improved simple prototype chain inheritance)

The fourth method is an improvement on the second method. Since immutable attributes in Animal objects can be written directly to animal. prototype. Cat() can skip Animal() and inherit Animal. Prototype.

The specific implementation

function Animal(){}
Animal.prototype.species = 'animals';
functionCat(name,color){ this.name = name; this.color = color; Cat. Prototype = animal. prototype; Cat.prototype.constructor = Cat; var cat1 = new Cat('heavy'.'yellow'); console.log(cat1.species); / / animalsCopy the code

The advantages and disadvantages

Advantages: Compared to the first method, this method has the advantage of being more efficient (no need to execute and build the Animal example) and less memory. Cons: Cat.prototype and animal. prototype now refer to the same object, so any changes to cat. prototype will be reflected in animal. prototype. The prototype. The constructor = Cat, the Animal. The prototype object constructor attributes also grew out of it

console.log(Animal.prototype.constructor); //CatCopy the code

Using empty objects as mediators (Parasitic composite Inheritance)

Because “Direct Inheritance of Prototype” suffers from the above shortcomings, the following method uses an empty object as a mediator.

function Animal(){}
Animal.prototype.species = 'animals';
functionCat(name,color){ Animal.call(this); this.name = name; this.color = color; } // Use empty objects as mediators, core var F =function() {}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat;Copy the code

F is an empty object, so it takes almost no memory. The Cat prototype object does not affect the Animal prototype object.

console.log(Animal.prototype.constructor); //AnimalCopy the code

Encapsulate the above methods into a function for easy use

function extend(Child,Parent){
	var F = function() {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; }Copy the code

The usage method is as follows:

function Animal(){}
Animal.prototype.species = 'animals';
function Cat(name,color){
	this.name = name;
	this.color = color;
}
extend(Cat,Animal);
var cat1 = new Cat('heavy'.'yellow'); console.log(cat1.species); / / animalsCopy the code

The last line of the function

Child.uber = Parent.prototype;
Copy the code

Give the child an Uber property that points directly to the prototype property of the parent. (Uber is a German word meaning “up,” “up a level.”) This is equivalent to opening a channel on the child to call the parent’s methods directly. This row right here is just for completeness of inheritance, it’s just a spare property.

Copy inheritance

The prototype object is used to implement inheritance. Alternatively, we can adopt a pure “copy” approach to inheritance. Simply put, all the properties and methods of the parent object are copied into the child object. Define a function for the purpose of copying attributes:

function extend(Child,Parent){
	var p = Parent.prototype;
	var c = Child.prototype;
	for(var i in p){
		c[i] = p[i];
	}
	c.uber = p;
}
Copy the code

The prototype function copies attributes from the parent object to the Child object’s Prototype. The specific implementation of inheritance is as follows:

function Animal(){}
Animal.prototype.species = 'animals';
function Cat(name,color){
	this.name = name;
	this.color = color;
}
extend(Cat,Animall);
var cat = new Cat('heavy'.'yellow'); console.log(cat.species); / / animalsCopy the code