This article understands class features and inheritance from a compiled code analysis of Babel.
Front knowledge
Object.setPrototypeOf(obj, prototype) / Object.getPrototypeOf(obj)
Set the obj prototype object to prototype/Get the obj prototype
Object.create(proto [, propertiesObject])
Create the prototype object as proto
// The easy version has no restrictions
Object.create = function(proto){
function F(){}
F.prototype = proto;
return new F();
}
Copy the code
Object.defineProperty
Add a new property to the object
Reflect.construct(target, args [, newTarget])
Equivalent to new target(… Args), which returns the Target constructor with the prototype pointing to newTarget.
Note: the function directly executes new.target as undefined, the new call has a value, and Reflect points to it automatically.
For example,
function OneClass() {
console.log('OneClass');
console.log(new.target);
}
OneClass.prototype.sayOne = function () { console.log('sayOne')}function OtherClass() {
console.log('OtherClass');
console.log(new.target);
}
OtherClass.prototype.sayOther = function () { console.log('sayOther')}var obj2 = Reflect.construct(OneClass, [], OtherClass);
// OneClass function OtherClass { ... }
/ / equivalent
var obj3 = Object.create(OtherClass.prototype);
OneClass.apply(obj3, []);
// OneClas undefined
Copy the code
Class is compiled
class Parent {
p = 'parent';
hiP = () = > {}
constructor(){
this.age = 10;
}
sayParent(){}
static hi(){}}Copy the code
After the transformation
function _classCallCheck(instance, Constructor) {
if(! (instanceinstanceof Constructor)) {
throw new TypeError("Cannot call a class as a function"); }}function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor); }}function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true.configurable: true.writable: true
});
} else {
obj[key] = value;
}
return obj;
}
var Parent = /*#__PURE__*/ (function () {
"use strict";
function Parent() {
_classCallCheck(this, Parent);
_defineProperty(this."p"."parent");
_defineProperty(this."hiP".function () {});
this.age = 10;
}
_createClass(
Parent,
[
{
key: "sayParent".value: function sayParent() {}}], [{key: "hi".value: function hi() {}}]);returnParent; }) ();Copy the code
Execution process:
When executed, initialize a Parent function, call _createClass, add methods to the constructor (static xx) and the prototype (methods defined in the class), and return the Parent function.
When new Parent(), the Parent function is executed and attributes are added internally to the instance.
ES6 class features
-
You must use the new call;
-
There is no variable promotion;
-
The default is strict mode;
-
All internally defined methods are non-enumerable;
-
Subclasses inherit from their parent class, which is called in the default constructor. When displaying the constructor statement, use super();
A class is a function expression when compiled by Babel
class Parent {}
function _classCallCheck(instance, Constructor) {
if(! (instanceinstanceof Constructor)) {
throw new TypeError("Cannot call a class as a function"); }}var Parent = function Parent() {
"use strict";
_classCallCheck(this, Parent);
};
Copy the code
It’s easy to see: the default is strict mode, new must be called, and an error is reported when a direct function calls Parent().
The purpose of class promotion is to ensure the validity of inheritance. Since class is compiled as a function expression, the following code fails:
new Parent();
class Parent {}
new Parent();
varParent = ... ;Copy the code
Parent is not defined.
class Parent {}
class Child extends Parent {}varParent = ... ;varChild = ... ;Copy the code
Now, if the class is promoted, when executing Child, the Child is in front of the Parent, and the Parent is not found, an error is reported.
All internally defined methods are not enumerable
class Parent {
sayParent(){}
static hi(){}}Object.keys(Parent) / / []
Object.keys(Parent.prototype) / / []
// Bable compiled key code can also be seen
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false; / / here
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor); }}Copy the code
inheritanceextends
/ / source
class Parent {
p = 'parent';
hiP = () = > {}
constructor(){
this.age = 10;
}
sayParent(){}
static hi(){}}class Child extends Parent{}Copy the code
/ / conversion
var Child = /*#__PURE__*/ (function (_Parent) {
"use strict";
_inherits(Child, _Parent);
var _super = _createSuper(Child);
function Child() {
_classCallCheck(this, Child);
return _super.apply(this.arguments);
}
return Child;
})(Parent);
// The inherited key code
function _inherits(subClass, superClass) {
if (typeofsuperClass ! = ="function"&& superClass ! = =null) {
throw new TypeError("Super expression must either be null or a function");
}
/ / prototype
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true.configurable: true}});// constructor
if (superClass) _setPrototypeOf(subClass, superClass);
}
Copy the code
Prototype, the prototype object of the Child is Parent, so that the Child can access static methods on the Parent. An instance of new Child() can access method properties on the Parent. Prototype object.
_createSuper returns a function:
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this.arguments);
}
return _possibleConstructorReturn(this, result);
};
}
Copy the code
New Child, execute that method, Derived is Child, so Super is Parent, return Parent instance
If a subclass does not declare constructor, the parent class is called by default
function Child() {
_classCallCheck(this, Child);
/ / the parent class
return _super.apply(this.arguments);
}
Copy the code
What happens when a subclass has constuctor?
- Didn’t write
super()
, will report an error, promptthis hasn't been initialised - super() hasn't been called
/ / source
class Child extends Parent{
constructor(){}}/ / conversion
var Child = /*#__PURE__*/ (function (_Parent) {
"use strict";
_inherits(Child, _Parent);
var _super = _createSuper(Child);
function Child() {
var _this;
_classCallCheck(this, Child);
return _possibleConstructorReturn(_this);
}
return Child;
})(Parent);
// Call is undefined _assertThisInitialized
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
} else if(call ! = =void 0) {
throw new TypeError(
"Derived constructors may only return object or undefined"
);
}
return _assertThisInitialized(self);
}
/ / the self is undefined
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return self;
}
Copy the code
- With super(), compile as follows, similar to without the constructor declaration
/ / source
class Child extends Parent{
constructor(){
super();
}
}
/ / conversion
var Child = /*#__PURE__*/ (function (_Parent) {
"use strict";
_inherits(Child, _Parent);
var _super = _createSuper(Child);
function Child() {
_classCallCheck(this, Child);
return _super.call(this);
}
return Child;
})(Parent);
Copy the code
- This in front of the super ()
class Child extends Parent{
constructor(){
this.child = 'dxx';
super();
}
}
var Child = /*#__PURE__*/ (function (_Parent) {
"use strict";
_inherits(Child, _Parent);
var _super = _createSuper(Child);
function Child() {
var _this;
_classCallCheck(this, Child);
_this.child = "dxx";
return (_this = _super.call(this));
}
return Child;
})(Parent);
Copy the code
New Child() _this = undefined, error: _this. Child = “DXX
- After this in super ()
class Child extends Parent{
constructor(){
super(a);this.child = 'dxx'; }}var Child = /*#__PURE__*/ (function (_Parent) {
// ...
function Child() {
var _this;
_classCallCheck(this, Child);
_this = _super.call(this);
_this.child = "dxx";
return _this;
}
return Child;
})(Parent);
Copy the code
Prove that Child instance _this is created by the parent class.
conclusion
We can see from the compiled code from Babel that classes are syntactic sugar and are actually function expressions. When subclass extends extends helps us implement inheritance, notice that we use this in the constructor after super() is called.
reference
MDN