The constructor pattern encapsulates objects

JavaScript is an object-based language, and almost everything you encounter is an object. However, it is not a true object-oriented programming (OOP) language because it does not have a true class in its syntax. ECMAScript2015 (ES6) introduces classes, but if you look closely at the classes in ES6, you will see that the classes in ES6 are just syntactic sugar. ES5 can do all that, but the new class notation just makes writing object prototypes much cleaner and more like object-oriented programming syntax.

So, how do we wrap property and Method into an object?

To solve the problem of generating instances from prototype objects, Javascript provides a Constructor pattern.

The “constructor” is just a normal function, but it uses the this variable inside. Using the new operator on the constructor generates the instance, and the this variable is bound to the instance object.

Let’s say we want to create a prototype object for a cat:

function Cat(name,color){
    this.name = name;
    this.color = color;
}
Copy the code

Now we can generate instance objects:

Var cat1 = new Cat(" ", "");Copy the code

Constructors are nice, but there is a memory waste problem

See, we now add a constant attribute type to the Cat object and a method eat. So, the prototype object Cat looks like this:

function Cat(name,color){ this.name = name; this.color = color; This. type = "cat "; This.eat = function(){console.log(" eat mouse "); this.eat = function(){console.log(" eat mouse "); }}Copy the code

When instantiating the object, the type attribute and the eat() method are identical for each instance object, and each time an instance is generated, it must consume more memory for the duplicate content. This is neither environmentally friendly nor efficient.

To solve this problem, you need to have type and eat() generated in memory only once, and then all instances point to that memory address.

Javascript states that each constructor has a Prototype property that points to another object. All properties and methods of this object are inherited by instances of the constructor.

This means that we can define those immutable properties and methods directly on the Prototype object.

function Cat(name,color){ this.name = name; this.color = color; } cat.prototype. type = "Cat "; Cat.prototype.eat = function(){console.log(" eat mouse "); }Copy the code

In this case, the type attribute and the eat() method of all instances are actually the same memory address, pointing to the Prototype object, thus making the operation more efficient.

Constructor inheritance

While the first part of the article described how to “encapsulate” data and methods, and how to generate instances from prototype objects, the second part begins with methods for inheritance between objects.

For example, we now have a constructor for an “animal” object:

Function Animal(){this.species = "Animal "; }Copy the code

There is also a constructor for the cat object

function Cat(name,color){
    this.name = name;
    this.color = color;
}
Copy the code

How can a cat inherit from an animal?

1. Borrow constructor inheritance

The first and simplest method is to bind the parent object’s constructor to the child, using the Call or apply methods, by adding a line to the child object’s constructor:

Function Animal() {this.species = "Animal "; } function Cat(name, color) { Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat(); console.log(cat1.species); / / animalsCopy the code

But the problem with this inheritance approach is that you can’t access properties and methods on the stereotype

2. Use Prototype chain Inheritance

Cat.prototype = new Animal(); Any Prototype object has a constructor attribute 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. Each instance has a constructor properties, invoke the prototype object by default constructor attribute the prototype. The constructor = Cat; Var cat1 = new Cat(" hairy "," yellow "); alert(cat1.species); / / animalsCopy the code

In this method, we manually correct the Constructor property, which is important to follow in future programming. If you replace a Prototype object, the next step must be to add the constructor property to the new Prototype object. And points the property back to the original constructor.

o.prototype = {};
o.prototype.constructor = o;
Copy the code

3. Inherit prototype directly

Function Animal(){} Animal.prototype.species = "Animal "; // Then the Cat prototype object points to the Animal prototype object and completes the inheritance. Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; Var cat1 = new Cat(" hairy "," yellow "); alert(cat1.species); / / animalsCopy the code

The advantage of doing this is that it is more efficient (no need to execute and create an instance of Animal) than the previous method, and saves a lot of energy. The downside is that cat. prototype and animal. prototype now refer to the same object, so any changes made to cat. prototype will be reflected in animal. prototype

So, the above code is actually a problem. Look at the second line:

Cat.prototype.constructor = Cat;
Copy the code

This statement changes the constructor property of the Amimal. Prototype object

4. Use empty objects as mediators

There are some drawbacks to “direct inheritance of Prototype” that can be solved by using an empty object as a mediator.

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.

We have encapsulated the above method 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; // The last line sets an uber property for the child, which 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. } // extend(Cat, Animal); Var cat1 = new Cat();Copy the code

5. Combinatorial inheritance

Call the parent class constructor, inherit the parent class attributes, by using the parent class instance as a subclass prototype, function reuse

function Cat(name, color) {
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
}

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

But there is a drawback to this approach: because the parent class is called twice, there are two instances

6. Inheritance Combination inheritance

Through the parasitic way (using empty objects as intermediaries), to fix the deficiency of combinative inheritance, to achieve perfect inheritance, we encapsulate two objects, to achieve inheritance, simulation of the real scene:

/ / parent function People (name, age) {enclosing name = name | | 'wangxiao enclosing the age = age | | 27} / / superclass method People. The prototype. Eat = Function (){return this.name + this.age + 'eat sleep'} // Subclass function Woman(name,age){// Inherit the parent class attribute People. Call (this,name,age)} var Super = function(){}; Super.prototype = People.prototype; Woman.prototype = new Super(); }) (); / / repair problem of constructors to Woman. The prototype. The constructor = Woman; var womanObj = new Woman();Copy the code