Personal Summary: Reading this article takes 15 minutes, the article mainly explains the working principle of Babel and typescript, (such as the conversion of ES6 class, is to convert the original ES6 code to ES5 code,
这些代码中包含着类似于 _classCallCheck 和 _createClass这样的函数,而这些函数已经
pre-defined in the standard libraries of Babel and typescript, and then processed).
By the way object.create this method, such as having a obj:{name: ' Is Ho ', f:function () {alert (1)}}
var a = object.create (obj)
At this point, the prototype of the A object is this obj
The above equals
var a
A = {}//q is an object
a.__proto__= obj
Class and inheritance and Babel and TypeScript code Transformation Quest
This is the 15th chapter of how JavaScript works.
Using classes to organize a variety of software engineering code today is the most common method. This chapter explores the different ways to implement JavaScript classes and how to build class inheritance. We will dig deeper into the prototype inheritance and analysis using popular class library simulations to implement class-based inheritance methods. Next, you'll learn how to use the translator to add non-native support syntax features to the language and how to use it in Babel and TypeScript to support the ECMAScript 2015 class. Finally, several examples of V8 native support implementation classes are presented.
Overview
JavaScript has no primitive type and everything is object. For example, the following string:
const name = "SessionStack";
You can immediately invoke the different methods on the newly created object:
console.log(a.repeat(2)); // 输出 SessionStackSessionStackconsole.log(a.toLowerCase()); // 输出 sessionstack
JavaScript is not the same as other languages, declaring a string or value automatically creates an object that contains values and provides different methods that can even run on the original type.
Another interesting fact is that complex data types such as arrays are also objects. When you use TypeOf to check an array instance, it is output object
. The index value of each element in the array is the property of the object. So when an element is accessed through an array index, it is actually accessing the properties of an array object and getting the property value. When it comes to data storage, the following two definitions are the same:
let names = [“SessionStack”];let names = { “0”: “SessionStack”, “length”: 1}
Therefore, the speed of accessing array elements and object properties is the same. I took a lot of detours to find the truth. There was a time when I had to make a lot of performance optimizations for some of the critical code in the project. After experimenting with other simple methods, I replaced all the objects with an array. Access to array elements is arguably faster than accessing hash's key values. However, I was surprised to find no improvement in performance. In JavaScript, all operations are implemented by accessing the keys in the hash graph and are time-consuming.
Using the prototype simulation class
When it comes to objects, it is the class that first reflects the eye. Developers are accustomed to using associations between classes and classes to organize programs. Although everything in JavaScript is object, it does not use classic class-based inheritance. Instead, you use prototypes to implement inheritance.
In JavaScript, each object associates its prototype object. When accessing a method or property of an object, first search the object itself. If it is not found, it is searched on the object prototype.
Let's take the example of defining the constructor for the underlying class:
function Component(content) { this.content = content;}Component.prototype.render = function() { console.log(this.content);}
Add the render function to the prototype so that the Component instance can use that method. When invoking the method of the Component class instance, first query the method on the instance. The rendering method is then found on the prototype.
Now, try extending the component class to introduce a new subclass.
function InputField(value) { this.content = `<input type="text" value="${value}" />`;}
If you want to Inputfield extend the method of the component class and call its Render method, you need to change its prototype. When invoking an instance method of a subclass, you certainly do not want to find it on an empty prototype (in fact, all objects have a common prototype, where the original text is not rigorous ). The lookup is continued on the Component class.
InputField.prototype = Object.create(new Component());
In this way, the Render method can be found on the prototype of the Component class. In order to implement inheritance, the Inputfield prototype needs to be set to an instance of the component class. Most libraries use object.setprototypeof to implement inheritance.
However, there are other things that need to be done. Each time you extend a class, you need to do the following:
- Set the prototype of a subclass as an instance of the parent class
- The parent class constructor is called in the constructor of the subclass so that the initialization logic of the parent class constructor can be executed.
- Introduces methods that access the parent class. When overriding a parent class method, you want to invoke the original implementation of the parent class method.
As you can see, when you want to implement all of the functions that are based on class inheritance, you need to perform such complex logical steps every time. When you need to create so many classes, that means you need to encapsulate these logic as reusable functions. This is the first thing developers can do to simulate class-based inheritance through various class libraries. These solutions are so popular that there is an urgent need for languages to integrate this functionality. This is why the first major revision of ECMAScript 2015 introduced the syntax to support the creation of classes based on class inheritance.
Class conversions
When new features are proposed in ES6 or ECMAScript 2015, the JavaScript developer community is eager to support the engine and browser implementations. A good way to implement this is through code conversion. It allows code to be written using ECMAScript 2015来 and then converted to JavaScript code that any browser can run. This includes using class-based inheritance to write classes and transform them into executable code.
Babel is one of the most popular converters. Let's understand the principle of transcoding by Babel converting the component class.
class Component { constructor(content) { this.content = content; } render() { console.log(this.content) }}const component = new Component(‘SessionStack‘);component.render();
The following is how Babel is converted to the class definition:
var Component = function () { function Component(content) { _classCallCheck(this, Component); this.content = content; } _createClass(Component, [{ key: ‘render‘, value: function render() { console.log(this.content); } }]); return Component;}();
As you can see, the code is converted into a ECMAScript 5 code that runs in any environment. Additionally, additional functions are introduced. They are part of the Babel standard library. The and functions are introduced into the compiled file _classCallCheck
_createClass
. The first function guarantees that a constructor is never called as a normal function. This is done by examining whether the function execution context is a Component object instance. The code checks to see if this points to such an instance. The second function _createClass
creates the properties of the object (class) by passing in an array of objects that contain the keys and values.
To understand how inheritance works, let's analyze the Inputfield subclasses that inherit from the Component class.
class InputField extends Component { constructor(value) { const content = `<input type="text" value="${value}" />`; super(content); }}
Here is the output of using Babel to process the above example:
var InputField = function (_Component) { _inherits(InputField, _Component); function InputField(value) { _classCallCheck(this, InputField); var content = ‘<input type="text" value="‘ + value + ‘" />‘; return _possibleConstructorReturn(this, (InputField.__proto__ || Object.getPrototypeOf(InputField)).call(this, content)); } return InputField;}(Component);
In this example, the inheritance logic is encapsulated in the _inherits function. It does the same thing as before, setting the prototype of the subclass to an instance of the parent class.
In order to convert the code, Babel performed several conversions. First, parse the ES6 code and translate it into an intermediate presentation layer called the syntax Abstraction tree, which has been talked about in the previous article. The tree is converted to a different syntax abstraction tree, and each node in the tree is converted to the corresponding ECMAScript 5 node. Finally, convert the syntax abstract tree to the ES5 code.
The syntax abstract tree in Babel
The AST consists of nodes, with only one parent node per node. There is an underlying type node in Babel. The node contains information about the contents of the node and its location in the code. There are various types of nodes, such as literal representation of strings, numeric values, null values, and so on. There is also a statement node for the control flow (if) and the loop (for, while). In addition, there is a special type of class node. It is a subclass of the underlying node class, and it expands itself by adding field variables to store references to the underlying class and the body of the class as a separate node.
Transform the following code snippet into a syntax abstract tree:
class Component { constructor(content) { this.content = content; } render() { console.log(this.content) }}
The following is a general case of the syntax abstract tree for this code fragment:
After the syntax abstraction tree is created, each node is converted to its corresponding ECMAScript 5 node and then converted to code that follows the ECMAScript 5 standard specification. This is done by looking for the node farthest from the root node and then converting to code. Their parent nodes are then converted to code by using code fragments generated by each child node, and so on. This process is known as Depth-first traversal, which is depth-first traversal.
In the example above, first generate two methoddefinition nodes, then the code of the class body node, and finally the code of the ClassDeclaration node.
Using TypeScript for conversion
TypeScript is another popular framework. It introduces a new syntax for writing JavaScript programs and then converts it to any browser or Emcascript 5 code that the engine can run. Here's the code to implement the component class using Typescript:
class Component { content: string; constructor(content: string) { this.content = content; } render() { console.log(this.content) }}
The following is a syntax abstract tree:
Inheritance is also supported.
class InputField extends Component { constructor(value: string) { const content = `<input type="text" value="${value}" />`; super(content); }}
The code conversion results are as follows:
var InputField = /** @class */ (function (_super) { __extends(InputField, _super); function InputField(value) { var _this = this; var content = "<input type=\"text\" value=\"" + value + "\" />"; _this = _super.call(this, content) || this; return _this; } return InputField;}(Component));
Similarly, the final result contains some class library code from TypeScript. __extends
has encapsulated the same inheritance logic as discussed in the first section.
With the widespread use of Babel and TypeScript, standard classes and class-based inheritance have become the standard way to organize JavaScript programs. This pushes the browser native support class.
Native support for Class
2014, Chrome native support class. This allows you to implement the syntax of the declaring class without using any libraries or converters.
The process of the native implementation of a class is called the syntactic sugar process. This is just an elegant syntax that can be converted to the same primitive that the language has long supported. Using the new, easy-to-use class definition is ultimately about creating constructors and modifying prototypes.
V8 Engine Support situation
Let's see how V8 natively supports the ES6 class. As discussed in the previous article, first parse the new syntax into a running JavaScript code and add it to the AST tree. The result of the class definition is to add a new node of type classliteral in the syntax abstraction tree.
The node contains some information. First, it treats the constructor as a separate function and contains the class property set. These properties can be a method, a getter, a setter, a public variable, or a private variable. The node also stores a pointer reference to the parent class, which also stores the constructor, the property set, and the parent class reference, and so on.
Once the new classliteral is converted to bytecode, it is converted into various functions and prototypes.
How JavaScript works (JavaScript works) (15) class and inheritance and Babel and TypeScript code conversion quest