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