Review OOP related concepts
- Encapsulation: Encapsulates data and methods inside a class as a whole
- Inheritance: Two classes establish a parent-child relationship, and subclasses get some members of the parent class
- Polymorphism: an inheritance of related classes whose objects can respond differently to the same method
- Override: A subclass rewrites a method that the implementation inherits
- Overloading: a class with multiple methods of the same name
JavaScript has no real classes and inheritance, but can only be emulated through prototypes and prototype chains.
Prototype chain
A stereotype chain is a chain of objects connected by a stereotype object via a __proto__ attribute to achieve property sharing.
Prototype is the explicit prototype property generated when the function is declared, and __proto__ is the instance property of the prototype pointing to the owning class.
Summary of rules:
- All functions (including Function) themselves are instances of Function.
- The next level in the prototype chain is an instance of the previous level.
- The end of the prototype chain is Object.prototype and its upper level is NULL.
Class concepts in JS
The composition of the class
- Constructor: Any non-arrow function
- Member: according to mount position can be divided into instance, prototype, static
Members can be data or methods
The characteristics of the class
- Dynamic inheritance: Changes to the stereotype affect related instance objects
- Writable, enumerable, configurable: These three configurations of properties can be edited after ES5
/ / es5 writing
function Cat(name) {
this.name = name; // Instance member, mounted on this
}
Cat.prototype.jump = function() {}; // The prototype member is mounted to the prototype object
Cat.generate = function() {}; // Static member attached to the class constructor
// Es6 provides syntactic sugar
class Cat {
constructor(name) {
this.name = name;
}
jump() {}
static generate() {}
}
// Babel transforms es5-loose
("use strict");
var Cat = /*#__PURE__*/ (function() {
function Cat(name) {
this.name = name;
}
var _proto = Cat.prototype;
_proto.jump = function jump() {};
Cat.generate = function generate() {};
returnCat; }) ();Copy the code
instantiation
Principle of New:
- To generate the object
- Link to the prototype
- Call the constructor to change the this pointer
- Returns the object
function fakeNew(constructor) {
var obj = {};
obj.__proto__ = constructor.prototype;
constructor.apply(obj, Array.from(arguments).slice(1, arguments.length));
return obj;
}
Copy the code
function Foo() {}
Foo.getName = function() {
console.log("1");
};
Foo.prototype.getName = function() {
console.log("2");
};
// Details: Calls using parameter lists have higher priority
new Foo.getName(); / / - > 1
new Foo().getName(); / / - > 2
Copy the code
inheritance
Parasitic inheritance
Parasitic inheritance + constructor inheritance is the best practice for JS inheritance. Es5 already has an easy-to-use API, and to understand its implementation, the concrete implementation of ES3 is also given here.
/ / es5 implementation
Cat.prototype = Object.create(Animal.prototype, {
constructor: "Cat"
});
// es3 implementation: create an inherited intermediate class bridge to bridge
const inherit = (function() {
// Use IIFE to create closures in heavy use cases to avoid the overhead of creating Temp repeatedly
function Temp() {}
return function(Child, Parent) {
Temp.prototype = Parent.prototype;
Child.prototype = newTemp(); Child.prototype.constructor = Child; }; }) ();Copy the code
Prototype chain inheritance
After looking at best practices, go back to prototype chain inheritance and understand its limitations.
var Cat;
function Animal() {
this.items = [];
}
// A) Stereotype chain inheritance: the parent class instance is the stereotype and the subclass instances share the instance
/ / the Cat instance - > __proto__ - > Animal instance - > __proto__ - > Object: {constructor: Animal, __proto__ : Object}
Cat = function() {};
Cat.prototype = new Animal();
new Cat().items.push(0);
new Cat().items; / / [0];
// the parent constructor is omitted
__proto__ --> Object: {constructor: Animal, __proto__: Object}
Cat = function() {};
Cat.prototype = Animal.prototype;
new Cat().items; // undefined
// C) parasitic inheritance scheme
/ / the Cat instance - > __proto__ - > Animal instance - > __proto__ - > Object: {constructor: Animal, __proto__ : Object}
Cat = function() {};
Cat.prototype = Object.create(Animal.prototype);
new Cat().items; // undefined
Copy the code
- Scheme A: Unnecessary instantiation of the parent class
- Scheme B: It is not strictly inheritance, but adds a constructor to the parent class.
From the prototype chain results, the results of A and C are consistent; Both B and C bypass the parent constructor.
Parasitic inheritance has an advantage over stereotype chain inheritance in that it removes, or reduces, the instantiation overhead (the ES3 implementation uses an empty constructor bridge).
Constructor inheritance
Constructor inheritance is an essential step of inheritance and is simple to implement. Use call and apply to simulate super.
function Cat() {
Animal.apply(this.arguments);
}
Copy the code
Inheritance combination relationship combing
ES6 implements the difference
Inheritance prior to ES6 essentially created an instance object of the subclass, this, and then applied the constructor of the superclass. The inheritance mechanism of ES6 is different. You need to call super first to get this constructed by the parent class. Otherwise, a reference error will be reported.
In particular, in ES6 inheritance, subclasses can inherit static methods from their parent class.
class Cat extends Animal {
constructor(name) {
// super();
this.name = name; // ReferenceError}}Copy the code