Class is a new concept in ES6 called “classes.” In JavaScript, where everything is an object, classes may not be the equivalent of classes in other object-oriented languages. In fact, class is more of a syntactic sugar (but not quite), and its functionality, Much of this can be emulated using ES5, and the Babel plugin is familiar enough to convert our class writing to ES5 writing.
This article, more from ES5 and ES6 comparison way, to sort out some content. The purpose of learning a thing is still to produce applications, ES5 prototype, prototype chain content is basically familiar, through this comparison to learn class, a purer understanding of it, the subsequent code will be more convenient to use.
class
Let’s start with an example 🌰
class A {
constructor(name, favor) {
this.tag = 'A'
this.lowName = 'aaa'
}
sayA() {
console.log(this.name)
}
}
class B extends A {
constructor() {
super()
this.lowName = 'bbb'
}
sayB(){
console.log(this.name)
}
}
const b = new B()
console.log(b)
Copy the code
So instead of doing a single print, go to the console and look at the new instance
Analyze the generated example if using ES5 to implement the above functionality
function A() { this.tag = 'A' this.lowName = 'aaa' } A.prototype.sayA = function() { console.log(this.name) } function B() { A.call(this) this.lowName = 'bbb' } B.prototype.sayB = function() { console.log(this.name) } B.prototype.__proto__ = A.prototype const b = new B() console.log(b)Copy the code
B. protoType = A.prototype is an error, which will cause the prototype object of A to be overwritten, and there will be no inheritance relationship between A and B
super
Subclasses of class inherit from their parent. Super must be explicitly called once from constructor otherwise an error is reported, and the this keyword should be used only after super is called otherwise an error is reported.
This shows that the constructor writing in ES5 is not quite the same as the class writing in ES6 for instantiating objects using new.
Before we talk about inheritance, let’s recall what happens after new when we instantiate an object
New after the constructor is called
- Create an object
- Refers the object’s __Prototype__ to the constructor’s prototype
- Bind this inside the constructor to the object
- Execute the internal logic of the constructor
- If the constructor returns an object internally, the return object (without the inheritance of step 2, i.e., the __proto__ of the instance does not refer to the constructor’s prototype, but depends on the __proto__ of the return object), If there is no return or it is not an object, the object created in the previous steps is returned.
Fifth, both ES5’s new constructor and ES6’s class behave the same if they return an object in a constructor.
Once you understand this process, you can then examine how inheritance is implemented between base classes and derived classes.
In the ES5 notation above, we can inherit the attributes of the exclusive parent class by forcing binding (apply, call) to the constructor that calls the parent class displayed in the subclass constructor. Calling the parent constructor at the beginning of the constructor or at the end of the constructor is fine, but we only need to worry about property overrides as needed.
In ES6 classes, the instance object of the this binding is created in the base class and passed down through the super() method until the constructor method is automatically added to the bottom derived class, even if it is omitted from the derived class. Execute super()).
The most obvious difference between ES5 and ES6class is that they behave differently when an object is returned in a constructor that is not the lowest level of derived classes
ES5
function A() {
this.tag = 'A'
this.lowName = 'aaa'
}
A.prototype.sayA = function() {
console.log(this.name)
}
function B() {
A.call(this)
this.lowName = 'bbb'
return {
b: 'bbb'
}
}
B.prototype.sayB = function() {
console.log(this.name)
}
B.prototype.__proto__ = A.prototype
function C() {
B.call(this)
this.lowName = 'ccc'
}
C.prototype.sayC = function() {
console.log(this.name)
}
C.prototype.__proto__ = B.prototype
var c = new C()
console.log(c)
Copy the code
ES6
class A { constructor() { this.tag = 'A' this.lowName = 'aaa' } sayA() { console.log(this.name) } } class B extends A { constructor() { super() this.lowName = 'bbb' return { b: 'bbb' } } sayB(){ console.log(this.name) } } class C extends B { constructor() { super() this.lowName = 'ccc' } sayC() { console.log(this.name) } } const c = new C() console.log(c)Copy the code
Because the B constructor manually changed the instance object passed down by super, the instance object containing the inheritance relationship was replaced. This also indicates that in the normal new call chain, the prototype class to which the __proto__ of this bound instance object refers belongs. It’s passed up by super. That is, this = object.create (c.prototype) is done before the instance Object of this binding is passed down through super. The new.target property introduced in ES6, which is actually C here, is the class that passes up
class A {
...
}
class B extends A {
constructor() {
super()
console.log(new.target === C )
...
}
}
class C extends B {
...
}
const c = new C()
//true
Copy the code
The difference between the ES5 and ES6 classes is due to the fact that classes are simulated only through the results, not at the behavior level. Some behaviors that are not in the examples will show differences. In Babel, the behavior of the two is basically the same and mimics the behavior of class fairly completely, so I’ve distilled the basic code below
function _createSuper(Derived) { var Super = Derived.__proto__ var result result = Super.apply(this) return typeof this === "object" ? this : result } function A() { this.tag = 'A' this.lowName = 'aaa' } function B() { var _this _this = _createSuper.call(this,B) _this.lowName = 'bbb' return _this } B.prototype.__proto__ = A.prototype B.__proto__ = A function C() { var _this _this = _createSuper.call(this,C) _this.lowName = 'ccc' return _this } C.prototype.__proto__ = B.prototype C.__proto__ = B var c = new C()Copy the code
The final C constructor object for the this binding depends on the recursive call to the parent constructor via call, and the return after execution.
__proto__ = B, also in keeping with the behavior of class, the __proto__ of a subclass refers to the parent class.
For the use of super, you can look directly at the introduction of super on MDN
super([arguments]); // Call the parent object/parent constructor super.functiononparent ([arguments]); // Call the method on the parent object/parent classCopy the code
Super is not only used to pass the instance object this needs to bind to, but also to access the prototype object of the parent class.
A static method
In contrast to ES5, the static keyword is added to class to define a static method.
class A {
constructor(name, favor) {
this.tag = 'A'
this.lowName = 'aaa'
}
sayA() {
console.log(this.name)
}
static sayTagName() {
console.log('my tag is A')
}
}
class B extends A {
constructor() {
super()
this.lowName = 'bbb'
}
sayB() {
console.log(this.name)
}
}
const b = new B()
console.log(b)
Copy the code
Methods defined by the static keyword are not inherited by the instance and can be called by the class.
The significance of these two points can be analyzed one by one
- Not inherited by instance: does not exist in the instance or its prototype
- Can be called by class: The method can be found under the constructor property of the class’s Prototype and called.
Actually, it’s pretty clear. You can print it
b.sayTagName() // Uncaught TypeError: b.sayTagNameis not a function b.__proto__.constructor.sayTagName() // 'my tag is A' A.prototype.constructor.sayTagName() // 'my tag is A' A.prototype.constructor.hasOwnProperty('sayTagName') // trueCopy the code
Note that static methods defined by static use this internally, which in fact refers to this class (accessible via the constructor property of the class’s prototype object).
class A {
constructor(name, favor) {
this.tag = 'A'
this.lowName = 'aaa'
}
sayA() {
console.log(this.lowName)
}
static sayTagName() {
console.log('my tag is A')
}
}
A.sayTagName() // 'my tag is A'
A.prototype.constructor.sayTagName() // 'my tag is A'
Copy the code
A === A.prototype.constructor // true
Copy the code
So, static methods defined inside a class through static can call each other inside the methods using this. If you need this to access properties and methods after using a new instance, you need to bind the scope using bind.
ES5 is easy to write with the static sample code from the beginning, just add a line
A.s ayTagName = function () {the console. The log (' my tag is A ')} / * or Amy polumbo rototype constructor. SayTagName = function () { console.log('my tag is A') } */Copy the code
This is implemented in Babel via Object.defineProperty()
Object.defineProperty(A, 'sayTagName', {
value: function sayTagName() { console.log('my tag is A') },
enumerable: false,
writable: true
})
Copy the code