Class: A constructor that transforms to a traditional class pattern

Source: Internet
Author: User

Objective

JS based on the prototype of the ' class ', has been the front-end of the code to call surprise, but close to the traditional mode use class keyword definitions appear, but make some front-end counterparts deeply regret and have a message: "Also my unique JS", "net do something no substance", "I do not have the class to other other class on the", Even "changed careers" and so on. There is a normal mood, after all, new knowledge means more time and energy costs, and not simply close your eyes to enjoy.

However, the history of the axis printing is still, for a class certain point is that you can not say to the interviewer: "Please, not the younger brother does not understand, just do not want to know, you change a question Bai!" "On the one hand although it is class only a grammatical sugar, but the extends improvement of inheritance is good." On the other hand, the new feature that might appear on ' class ' In the future should be class hosted by rather than a constructor, and no one is sure how beautiful it will be in the future. So come and drink this steaming bowl of brown sugar ginger soup slowly.

1 class

There is no concept of class in ECMAScript, our instance is based on prototypes generated by constructors with dynamic properties and methods of objects. But in order to conform with the international, the description of the more simple and tall, will still use the word ' class '. So the JS class is equivalent to the constructor. ES6 is class just a syntactic sugar, and its definition of the resulting object is still a constructor. But to distinguish it from the constructor pattern, we call it class mode. Learning class needs to have the structure function and the prototype object knowledge, the concrete can own Baidu.

// ---使用构造函数function C () {  console.log(‘New someone.‘);}C.a = function () { return ‘a‘; }; // 静态方法C.prototype.b = function () { return ‘b‘; }; // 原型方法// ---使用classclass C {  static a() { return ‘a‘; } // 静态方法    constructor() { console.log(‘New someone.‘); } // 构造方法    b() { return ‘b‘; } // 原型方法};
1.1 Comparison with variables

A keyword is class similar to a keyword that defines a function function , which is defined in two ways: declarative and expression (anonymous and named). The variables defined by the declaration are of a different nature and are function more similar and do not let parse in advance, const do not have variable elevation, are not linked to global scopes, have temporary dead zones, and so on. classdefining a generated variable is a constructor, and therefore, a class can be written as a pattern of immediate execution.

// ---声明式class C {}function F() {}// ---匿名表达式let C = class {};let F = function () {};// ---命名表达式let C = class CC {};let F = function FF() {};// ---本质是个函数class C {}console.log(typeof C); // functionconsole.log(Object.prototype.toString.call(C)); // [object Function]console.log(C.hasOwnProperty(‘prototype‘)); // true// ---不存在变量提升C; // 报错,不存在C。class C {}// 存在提前解析和变量提升F; // 不报错,F已被声明和赋值。function F() {}// ---自执行模式let c = new (class {})();let f = new (function () {})();
1.2 Comparison with objects

The class content ( {} inside) is similar in form to the object literal. However, the contents of the class can only be defined methods can not define the property, the form of the method can only be abbreviated function, the method can not be separated by commas. The method name can be an expression with parentheses, or it can be a Symbol value. Methods are divided into three categories, the method of construction ( constructor method), the prototype method (which exists on the property of the constructor prototype ), and the static method (which exists on the constructor itself)

class C {  // 原型方法a  a() { console.log(‘a‘); }  // 构造方法,每次生成实例时都会被调用并返回新实例。  constructor() {}  // 静态方法b,带static关键字。  static b() { console.log(‘b‘); }  // 原型方法,带括号的表达式  [‘a‘ + ‘b‘]() { console.log(‘ab‘); }  // 原型方法,使用Symbol值  [Symbol.for(‘s‘)]() { console.log(‘symbol s‘); }}C.b(); // blet c = new C();c.a(); // ac.ab(); // abc[Symbol.for(‘s‘)](); // symbol s

Attributes cannot be defined directly, and do not indicate that a class cannot have prototypes or static properties. Parsing class forms a constructor, so you simply add the class as you would add a property to the constructor. It is also recommended to use only getter functions to define read-only properties. Why can't I set properties directly? Is it technically immature? Is the official hoping to pass on some kind of thought? Or is it just a question that I throw at random?

// ---直接在C类(构造函数)上修改class C {}C.a = ‘a‘;C.b = function () { return ‘b‘; };C.prototype.c = ‘c‘;C.prototype.d = function () { return ‘d‘; };let c = new C();c.c; // cc.d(); // d// ---使用setter和getter// 定义只能获取不能修改的原型或静态属性class C {  get a() { return ‘a‘; }  static get b() { return ‘b‘; }}let c = new C();c.a; // ac.a = ‘1‘; // 赋值没用,只有get没有set无法修改。
1.3 Comparison with constructors

The following is code that uses constructors and classes to implement the same functionality. Intuitively, the class code is simplified to make the content more aggregated. The constructor method body equals the function body of the constructor, and if this method is not explicitly defined, an empty constructor method is added by default to return the new instance. As with ES5, you can also customize the return of another object instead of a new instance.

// ---构造函数function C(a) {  this.a = a;}// 静态属性和方法C.b = ‘b‘;C.c = function () { return ‘c‘; };// 原型属性和方法C.prototype.d = ‘d‘;C.prototype.e = function () { return ‘e‘; };Object.defineProperty(C.prototype, ‘f‘, { // 只读属性  get() {    return ‘f‘;  }});// ---类class C {  static c() { return ‘c‘; }    constructor(a) {    this.a = a;  }    e() { return ‘e‘; }  get f() { return ‘f‘; }}C.b = ‘b‘;C.prototype.d = ‘d‘;

A class is a function, but cannot be new called directly by generating an instance. All methods defined inside the class are non-enumerable, and the properties and methods added on the constructor itself and prototype above are enumerable. The methods defined internally by the class are strictly schema by default and do not require explicit declaration. The above three points increase the rigor of the class, but unfortunately, there is still no way to directly define the private properties and methods.

 //---can directly call class C {}c ();//Error function C () {}c ();//CAN//---can enumerate class C {static A () {}//non-enumerable B () {}// Non-enumerable}c.c = function () {}; Enumerable C.PROTOTYPE.D = function () {}; Enumerable isenumerable (c, [' A ', ' C ']); A false, C trueisenumerable (C.prototype, [' B ', ' d ']);  b false, D truefunction isenumerable (target, keys) {Let obj = object.getownpropertydescriptors (target); Keys.foreach (k =>  {Console.log (k, obj[k].enumerable); });}    ---is strict mode class C {A () {let is = false;    try {n = 1;    } catch (e) {is = true; } console.log (is?)  ' True ': ' false ');  }}c.prototype.b = function () {Let is = false;  try {n = 1;  } catch (e) {is = true; } console.log (is?) ' True ': ' false ');}; Let C = new C (); C.A (); True, is strict mode. C.B (); False, not strict mode. 

Adding a keyword before the method static means that this method is a static method, which exists in the class itself and cannot be accessed directly by the instance. The static method points to the this class itself. Static methods and prototype methods can have duplicate names because they are on different objects. ES6 has added a command new.target that refers to the new following constructor or class that the use of the command has some limitations, see the example below.

// ---staticclass C {  static a() { console.log(this === C); }  a() { console.log(this instanceof C); }}let c = new C();C.a(); // truec.a(); // true// ---new.target// 构造函数function C() {  console.log(new.target);}C.prototype.a = function () { console.log(new.target); };let c = new C(); // 打印出Cc.a(); // 在普通方法中为undefined。// ---类class C {  constructor() { console.log(new.target); }  a() { console.log(new.target); }}let c = new C(); // 打印出Cc.a(); // 在普通方法中为undefined。// ---在函数外部使用会报错new.target; // 报错
2 extends

The classic inheritance method in ES5 is parasitic combined inheritance, which inherits the attributes and methods on the parent class instance and the prototype, respectively. The same is true of the nature of inheritance in ES6, but the way it is implemented has changed, as in the code below. As you can see, the inheritance on the prototype is in the form of using the extends keyword, which is closer to the traditional language, and the inheritance on the instance is done by invoking the super subclass this shape. On the surface, the way is more unified and concise.

class C1 {  constructor(a) { this.a = a; }  b() { console.log(‘b‘); }}class C extends C1 { // 继承原型数据  constructor() {    super(‘a‘); // 继承实例数据  }}
2.1 Comparison with constructors

Using extends inheritance not only prototype sets the prototype object () of the subclass's properties __proto__ to the parent class prototype , but also sets the prototype object () of the subclass itself to the __proto__ parent class itself. This means that the subclass inherits not only the prototype data of the parent class, but also the static properties and methods owned by the parent class itself. The classic inheritance of ES5 inherits only the prototype data of the parent class. Not only is wealth, even Dad's fame to obtain, good good.

class C1 {  static get a() { console.log(‘a‘); }  static b() { console.log(‘b‘); }}class C extends C1 {}// 等价,没有构造方法会默认添加。class C extends C1 {  constructor(...args) {    super(...args);  }}let c = new C();C.a; // a,继承了父类的静态属性。C.b(); // b,继承了父类的静态方法。console.log(Object.getPrototypeOf(C) === C1); // true,C的原型对象为C1console.log(Object.getPrototypeOf(C.prototype) === C1.prototype); // true,C的prototype属性的原型对象为C1的prototype

The instance inheritance in ES5 is to create an instance object of the subclass, and this then call apply Add the this instance properties and methods of the parent class on the or method. Of course, you can choose not to inherit the instance data of the parent class. And ES6 is different, its design makes instance inheritance more excellent and rigorous.

In ES6 instance inheritance, the method is called first to super create the parent class this (which still points to the child class) and to add the instance data of the parent class, which is then decorated with the constructor of the subclass, which is the this opposite of ES5. ES6 specifies that in the subclass constructor method, the method must be called before it is used to this super get the subclass this . The method is not called super , which means that the subclass cannot get the this object.

class C1 {  constructor() {    console.log(‘C1‘, this instanceof C);  }}class C extends C1 {  constructor() {    super(); // 在super()之前不能使用this,否则报错。    console.log(‘C‘);  }}new C(); // 先打印出C1 true,再打印C。
2.2 Super

Keywords are super more wonderful, in different environments and use the way, it will refer to different things (the total can refer to the object or method two). And when not explicitly indicated is used as an object or method, for example console.log(super) , will be directly error.

When used as a function. supercan exist only in the constructor of a subclass, when it refers to the parent class constructor.

When you are an object. superin a static method, the parent class itself, in the constructor method and the prototype method, is the property of the parent class prototype . However super , by calling the parent class method, the method this still points to the child class. That is, by super invoking the static method of the parent class, the method points to the this subclass itself, and the method this points to the instance of the (subclass) when it calls the parent class's prototype method. and by super Assigning a value to a property, in the subclass of the prototype method to refer to the instance, in the subclass of the static method refers to the subclass itself, after all, directly in the subclass by super Modifying the parent class is very dangerous.

Very confused, right, crazy, or the combination of code to see it!

class C1 {  static a() {    console.log(this === C);  }  b() {    console.log(this instanceof C);  }}class C extends C1 {  static c() {    console.log(super.a); // 此时super指向C1,打印出function a。        this.x = 2; // this等于C。    super.x = 3; // 此时super等于this,即C。    console.log(super.x); // 此时super指向C1,打印出undefined。    console.log(this.x); // 值已改为3。    super.a(); // 打印出true,a方法的this指向C。  }  constructor() {    super(); // 指代父类的构造函数        console.log(super.c); // 此时super指向C1.prototype,打印出function c。    this.x = 2; // this等于新实例。    super.x = 3; // 此时super等于this,即实例本身。    console.log(super.x); // 此时super指向C1.prototype,打印出undefined。    console.log(this.x); // 值已改为3。    super.b(); // 打印出true,b方法的this指向实例本身。  }}
2.3 Inheriting native constructors

Using the constructor pattern, building a subclass that inherits the native data structure (for example Array ) has many drawbacks. On the one hand, it is known from the above that primitive inheritance is the first to create a subclass this and then be decorated by the parent class constructor, so that the internal properties of the parent class (hidden property) cannot be obtained. On the other hand, the native constructor is ignored directly call or apply passed in by the method this , causing the subclass to not get the instance properties and methods of the parent class at all.

function MyArray(...args) {  Array.apply(this, args);}MyArray.prototype = Array.prototype;// MyArray.prototype.constructor = MyArray;let arr = new MyArray(1, 2, 3); // arr为对象,没有储存值。arr.push(4, 5); // 在arr上新增了0,1和length属性。arr.map(d => d); // 返回数组[4, 5]arr.length = 1; // arr并没有更新,依旧有0,1属性,且arr[1]为5。

The process of creating a class is to first construct a parent class that points to a subclass this (a bypass) and then decorate it with the constructor of the parent class and subclass. Therefore, you can circumvent the problem of constructors, get the instance properties and methods of the parent class, including the internal properties. In turn, a subclass of the native data structure is actually created, thus simply extending the native type. You can also set Symbol.species properties so that the derived object is a native class instead of a custom subclass instance.

class MyArray extends Array { // 实现是如此的简单  static get [Symbol.species]() { return Array; }}let arr = new MyArray(1, 2, 3); // arr为数组,储存有1,2,3。arr.map(d => d); // 返回数组[1, 2, 3]arr.length = 1; // arr正常更新,已包含必要的内部属性。

It is important to note that the subclass of the inheritance Object . ES6 changes the Object behavior of the constructor, and once it is found that it is not new Object() called in this form, the constructor ignores the arguments passed in. This causes Object subclasses to not initialize properly, but this is not a big problem.

class MyObject extends Object {  static get [Symbol.species]() { return Object; }}let o = new MyObject({ id: 1 });console.log(o.hasOwnPropoty(‘id‘)); // false,没有被正确初始化
Recommended

ES6 Essence: Symbol
ES6 Essence: Promise
Async: A simple and elegant way to Async
True operator of the GENERATOR:JS executive right

Class: A constructor that transforms to a traditional class pattern

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.