JavaScript uses stereotype inheritance: each object inherits properties and methods from the stereotype object.

The traditional Class that serves as a blueprint for creating objects in languages like Java or Swift does not exist in JavaScript. Stereotype inheritance deals only with objects.

Stereotype inheritance can mimic classical class inheritance. To bring traditional classes to JavaScript, the ES2015 standard introduces the class syntax: a syntactic sugar on top of stereotype inheritance.

This article familiarizes you with JavaScript classes: how to define classes, initialize instances, define fields and methods, understand private and public fields, and master static fields and methods.

1. Definition:classThe keyword

The JavaScript keyword class is used to define classes:

class User {
  // The body of class
}
Copy the code

The above code defines a class User. Curly braces {} mark the class body. Notice that this syntax is called a class declaration.

Instead of specifying a class name, you can assign a class to a variable by using a class expression:

const UserClass = class {
  // The body of class
};
Copy the code

You can easily export class as part of an ES2015 module. Here is a “default export” syntax:

export default class User {
 // The body of class
}
Copy the code

Named export:

export class User {
  // The body of class
}
Copy the code

Class becomes very useful when you create an instance. An instance is an object that contains the data and behavior described by a class.

JavaScript’s new operator is used to instantiate class: instance = new class ().

For example, you can instantiate the User class with the new operator:

const myUser = new User();
Copy the code

New User() creates an instance of the User class.

2. Constructor:constructor()

constructor(param1, param2, …) Is a special way to initialize instances within a class. This is where field initials are set or object Settings are made.

The following example sets the initial value of the name field in the constructor:

class User {
  constructor(name) {    this.name = name;  }}
Copy the code

The constructor of User takes an argument, name, that sets the initial value of the field this.name.

The this value inside the constructor is equal to the newly created instance.

The arguments used to instantiate the class become constructor arguments:

class User { constructor(name) { name; / / = >'Jon Snow'    this.name = name;
  }
}

const user = new User('Jon Snow');
Copy the code

The value of the name argument inside the constructor is ‘Jon Snow’.

If a class constructor is not defined, a default constructor is created. The default constructor is an empty function that does not modify the instance.

Also, JavaScript classes can have at most one constructor.

3. The field

Class fields are variables that hold information. Fields can be attached to two entities:

  1. Class instance field
  2. Class own field (i.e. static field)

Fields have two levels of accessibility:

  1. Public: Fields can be accessed arbitrarily
  2. Private: only accessible within the class

3.1 Public Instance Field

Let’s look at the previous code:

class User { constructor(name) { this.name = name; }}Copy the code

The expression this.name = name creates an instance field name and sets the initial value.

The name field can then be accessed as a property:

const user = new User('Jon Snow'); user.name; / / = >'Jon Snow'
Copy the code

Name is a public field because you can access it outside of the User class.

When fields are created implicitly in constructors, as in the previous example, it can be difficult to manage the list of fields. You have to decipher them from the constructor code.

A better approach is to explicitly declare the class field. No matter what the constructor does, the instance always has the same list of fields.

The Class field proposal allows you to define fields in the class body. Alternatively, you can specify the initial value immediately:

class SomeClass {
  field1;  
  field2 = 'Initial value';
  // ...
}
Copy the code

Let’s modify the User class to declare a public field name:

class User {
  name;  
  constructor(name) {
    this.name = name;
  }
}

const user = new User('Jon Snow'); user.name; / / = >'Jon Snow'
Copy the code

The name in the class body; A public field name is declared.

Public fields declared this way are expressive: a quick look at the field declaration is enough to know the data structure of the class.

Furthermore, class fields can be initialized immediately upon declaration.

class User {
  name = 'Unknown';
  constructor() { // No initialization } } const user = new User(); user.name; / / = >'Unknown'
Copy the code

Name = ‘Unknown’ in the class body declares a name field and sets the initial value ‘Unknown’.

There are no restrictions on access and updates to public fields. Public fields can be read and assigned outside constructors, methods, and classes.

3.2 Private instance fields

Encapsulation is an important concept that hides the details inside a class. People who use encapsulated classes rely only on the public interface provided by the class, and are not coupled to the implementation details of the class.

Class is organized with encapsulation in mind, making it easier to update when implementation details change.

A good way to hide data inside an object is to use private fields. These fields can only be read and changed in the class to which they belong. Private fields cannot be changed directly outside the class.

Private fields can only be accessed inside the class.

A field name can be made private by preceded by a special character #, such as #myField. The prefix # must be retained every time this field is used: when declaring, when reading, and when modifying.

Let’s make sure field #name can be set once at instance initialization:

class User {
  #name;
  constructor(name) {
    this.#name = name;
  }

  getName() {
    return this.#name;
  }
}

const user = new User('Jon Snow'); user.getName(); / / = >'Jon Snow'

user.#name; // Throws a SyntaxError
Copy the code

#name is a private field. You can access and modify # names inside User. The getName() method can access the private field #name.

But if you try to access the Private variable #name from outside the User class, you will throw a SyntaxError: SyntaxError: Private field ‘#name’ must be declared in an enclosing class.

3.3 Public Static Fields

You can also define fields on the class itself: static fields. This helps define class constants or store information specific to that class.

To create a static field in a JavaScript class, use the special keyword static plus the field name: static myStaticField.

Let’s add a new field type that represents the user type: admin or regular. Static fields TYPE_ADMIN and TYPE_REGULAR are constants that distinguish user types:

class User {
  static TYPE_ADMIN = 'admin';  static TYPE_REGULAR = 'regular';
  name;
  type;

  constructor(name, type) {
    this.name = name;
    this.type = type;
  }
}

const admin = new User('Site Admin', User.TYPE_ADMIN); admin.type === User.TYPE_ADMIN; / / = >true
Copy the code

Static TYPE_ADMIN and static TYPE_REGULAR define static variables inside the User class. To access static fields, you must use the class name plus the field name: user.type_admin and user.type_regular

3.4 Private Static Fields

Sometimes even static fields are implementation details you want to hide. You can also make static fields private here.

To make a static field private, just prefix the field name with the special symbol # : static #myPrivateStaticField.

Suppose you want to limit the number of instances of the User class. To hide the details of instance restrictions, you can create private static fields:

class User {
  static #MAX_INSTANCES = 2; static #instances = 0;
  name;

  constructor(name) {
    User.#instances++;
    if (User.#instances > User.#MAX_INSTANCES) {
      throw new Error('Unable to create User instance');
    }
    this.name = name;
  }
}

new User('Jon Snow');
new User('Arya Stark');
new User('Sansa Stark'); // throws Error
Copy the code

The static field User.#MAX_INSTANCES sets the maximum number of instances allowed, and the static field User.# Instances is the actual number of instances created.

Private static fields can only be accessed inside the User class. An external scope cannot interfere with the limiting mechanism here: this is the benefit of encapsulation.

Method 4.

Fields contain data. But the ability to modify data is provided by special functions that are part of a class: methods.

JavaScript classes support both instance methods and static methods.

4.1 Instance Method

Instance methods can access and modify instance data. Instance methods can call other instance methods as well as any static method.

For example, we define a getName() method in the User class that returns name:

class User {
  name = 'Unknown';

  constructor(name) {
    this.name = name;
  }

  getName() {    return this.name;  }}

const user = new User('Jon Snow'); user.getName(); / / = >'Jon Snow'
Copy the code

getName() { … } is a method in the User class. User.getname () is a method call: it executes the method and returns the calculated value, if any.

In class methods and constructors, the value of this is equal to the instance of the class. You can use this to access instance data: this.field, or call another method: this.method().

Let’s add a nameContains(STR) method that takes an argument and calls another method:

class User {
  name;

  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }

  nameContains(str) {   
 return this.getName().includes(str);  }}

const user = new User('Jon Snow');
user.nameContains('Jon'); / / = >true
user.nameContains('Stark'); / / = >false
Copy the code

nameContains(str) { … } is a method of the User class that takes a single argument STR. In addition, it executes another instance method, this.getName(), to get the user’s name.

Methods can also be private. To make a method private, prefix the name with # :

class User {
  #name;

  constructor(name) {
    this.#name = name;
  }

  #getName() { return this.#name; }
  nameContains(str) {
    return this.#getName().includes(str); }
}

const user = new User('Jon Snow');
user.nameContains('Jon'); / / = >true
user.nameContains('Stark'); / / = >false

user.#getName(); // SyntaxError is thrown
Copy the code

#getName() is a private method. Inside the nameContains(STR) method, call the private method this.#getName().

Because it is private, #getName() cannot be called outside the User class.

4.2 getters and setters

Getters and setters simulate regular fields, but have more control over how fields are accessed and changed.

Getters are executed when trying to get the value of a field, and setters are executed when trying to set the value.

To ensure that User’s name property is not empty, let’s wrap the private field #nameValue in getters and setters:

class User {
  #nameValue;

  constructor(name) {
    this.name = name;
  }

  get name() {   
     return this.#nameValue;
  }

  set name(name) {    
    if (name === ' ') {
      throw new Error(`name field of User cannot be empty`);
    }
    this.#nameValue = name;
  }
}

const user = new User('Jon Snow');
user.name; // The getter is invoked, => 'Jon Snow'
user.name = 'Jon White'; // The setter is invoked

user.name = ' '; // The setter throws an Error
Copy the code

get name() {… } Getter executes when you access field user.name.

And the set name (name) {… } execute when the field is updated user.name = ‘Jon White’. If the new value is an empty string, the setter throws an error.

4.3 Static Method

Static methods are methods that are directly attached to a class. They contain the logic associated with the class, not the instance of the class.

To create a static method, use the special keyword static followed by the normal method syntax: static myStaticMethod() {… }

There are two simple rules to keep in mind when using static methods:

  1. Static methods can access static fields
  2. Static methods cannot access instance fields

For example, let’s create a static method that checks if a user name is in use.

class User {
  static #takenNames = [];

  static isNameTaken(name) {   
    return User.#takenNames.includes(name);  
  }
   name = 'Unknown';

   constructor(name) {
    this.name = name;
    User.#takenNames.push(name);
  }
}

const user = new User('Jon Snow');

User.isNameTaken('Jon Snow'); / / = >true
User.isNameTaken('Arya Stark'); / / = >false
Copy the code

IsNameTaken () is a static method that uses the static private field User.#takenNames to check for occupied names.

Static methods can be private: static #staticFunction() {… }. Again, they follow the private rule: private static methods can only be called from within a class.

5. Inheritance:extends

JavaScript classes support single inheritance using the extends keyword.

In the class Child extends Parent {} statement, the Child class extends the constructors, fields, and methods of the Parent class.

For example, let’s create a subclass ContentWriter that inherits from the parent class User.

class User {
  name;

  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

class ContentWriter extends User {  posts = [];
}

const writer = new ContentWriter('John Smith'); writer.name; / / = >'John Smith'writer.getName(); / / = >'John Smith'writer.posts; / / = > []Copy the code

ContentWriter inherits the constructor, method getName(), and field name from User. The ContentWriter class also declares a new field, posts.

Note that private members of a parent class cannot be inherited by subclasses.

5.1 Superclass constructor:constructor()In thesuper()

If you want to call the parent constructor in a subclass, you need to use the special super() method in the subclass constructor.

For example, we let the ContentWriter constructor call the User constructor and initialize the posts field:

class User {
  name;

  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

class ContentWriter extends User {
  posts = [];

  constructor(name, posts) {
    super(name);    
    this.posts = posts;
  }
}

const writer = new ContentWriter('John Smith'['Why I like JS']); writer.name; / / = >'John Smith'
writer.posts // => ['Why I like JS']
Copy the code

The super(name) in the ContentWriter subclass performs the constructor of the User superclass.

Note that in the subclass constructor you must call super() before using the this keyword. The parent constructor is not guaranteed to be instantiated until super() is called.

Class Child extends Parent {constructor(value1, value2) {this.prop2 = value2; super(value1); }}Copy the code

5.2 Parent class instance: in the methodsuper

If you want to access a parent method in a subclass method, you can use the special shortcut super.

class User {
  name;

  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

class ContentWriter extends User {
  posts = [];

  constructor(name, posts) {
    super(name);
    this.posts = posts;
  }

  getName() {
    const name = super.getName();    
    if (name === ' ') {
      return 'Unknwon';
    }
    return name;
  }
}

const writer = new ContentWriter(' '['Why I like JS']); writer.getName(); / / = >'Unknwon'
Copy the code

GetName () in the ContentWriter subclass accesses the method super.getName() of the User superclass.

This feature is called method rewriting.

Note that you can also use super in static methods to access the static methods of the parent class.

6. Object type detection:instanceof

Object instanceof Class is the operator used to determine whether an object is an instanceof Class.

Let’s see how instanceof is actually used:

class User {
  name;

  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

const user = new User('Jon Snow'); const obj = {}; user instanceof User; / / = >trueobj instanceof User; / / = >false
Copy the code

User is an instanceof the user class, so the value of user instanceof user is true.

The null object {} is not an instanceof User, and the corresponding obj instanceof User is false.

Instanceof is polymorphic: this operator considers instances of subclasses to be instances of superclasses.

class User {
  name;

  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

class ContentWriter extends User {
  posts = [];

  constructor(name, posts) {
    super(name);
    this.posts = posts;
  }
}

const writer = new ContentWriter('John Smith'['Why I like JS']); writer instanceof ContentWriter; / / = >truewriter instanceof User; / / = >true
Copy the code

Writer is an instance of the ContentWriter subclass. The writer instanceof ContentWriter operator is set to true.

Also, ContentWriter is a subclass of User, so writer instanceof User is true.

What do you do if you want to determine the exact class of an instance? You can use the constructor attribute and compare it directly to class:

writer.constructor === ContentWriter; / / = >truewriter.constructor === User; / / = >false
Copy the code

7. Classes and prototypes

It must be said that JavaScript’s class syntax abstracts the stereotype inheritance mechanism nicely. I don’t even use the word “prototype” to describe class syntax.

But classes are built on top of prototype inheritance. Each class is a function and creates an instance when called as a constructor.

The following two pieces of code are equivalent.

Class version:

class User {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

const user = new User('John'); user.getName(); / / = >'John Snow'user instanceof User; / / = >true
Copy the code

The prototype version:

function User(name) {
  this.name = name;
}

User.prototype.getName = function() {
  return this.name;
}

const user = new User('John'); user.getName(); / / = >'John Snow'user instanceof User; / / = >true
Copy the code

The Class syntax is easier to use if you are familiar with the classic inheritance mechanisms of the Java or Swift languages.

Anyway, even if you’re using class syntax in JavaScript, I recommend that you master prototype inheritance, okay

8. Availability of class features

The class features mentioned in this article appear in the ES2015 and Stage 3 proposals.

By the end of 2019, class features are distributed in the following proposals and standards:

  • Public and private instance fields belong to the Class field proposal
  • Private instance methods and accessors belong to the Class private method proposal
  • Public and private static fields and private static methods are part of the Class static feature proposal
  • The rest belong to ES2015 standards.

9. To summarize

JavaScript classes use constructors to initialize instances and define fields and methods. You can even append fields and methods to classes using the static keyword

Inheritance is implemented through the extends keyword: you can easily create subclasses from a parent class. The super keyword is used by subclasses to access their parent classes.

To take advantage of encapsulation, make fields and methods private to hide the inner details of the class. Private field and method names must begin with #.

Classes in JavaScript are becoming easier to use.

To the private property#Prefixes, what do you think?

The original author: Dmitri Pavlutin: dmitripavlutin.com/javascript-… Translation: 1024 translation station

More front-end technology dry goods in wechat public number: 1024 translation station