This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging
Traditional JavaScript programs use functions and prototype-based inheritance to create reusable components, but this can be tricky for programmers familiar with the object-oriented approach, because they use class-based inheritance and objects are built from classes. Starting with ECMAScript 2015, also known as ECMAScript 6, JavaScript programmers will be able to use a class-based object-oriented approach.
class Greeter {
greeting: string; // A property called greeting
constructor(message: string) { // The constructor
this.greeting = message;
}
greet() { / / the greet method
return "Hello, " + this.greeting; }}let greeter = new Greeter("world"); // The type of instance greeter is greeter
Copy the code
inheritance
One of the most basic patterns in class-based programming is to allow the use of inheritance to extend existing classes
class Animal {
name: string;
constructor(name: string) { this.name = name; }
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`); }}class Dog extends Animal {
constructor(name: string) {
// The super method must be called before the constructor can access the property of this
super(name);
}
// Replace the superclass method
move(distanceInMeters: number = 0) {
console.log(`The ${this.name} moved ${distanceInMeters}m.`);
}
bark() {
console.log('Woof! Woof! '); }}const dog = new Dog('Steven');
dog.bark(); // succes
dog.move(10); // success
const dog1: Animal = new Dog('Jhon');
dog1.bark(); // Error: the dog1 type is Animal
// The actual execution result is Jhon Moved 5m.
// ts only restricts the type, so dog1 is still an instance of Dog when it is actually run
dog1.move(5); // success
Copy the code
In this case, Dog is a _ derived class _, which is derived from the Animal base class through the extends keyword. Derived classes are often called _ subclasses _, and base classes are often called _ superclasses _ or superclasses.
Member modifier
public
Public is the default
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number) {
console.log(`The ${this.name} moved ${distanceInMeters}m.`); }}Copy the code
Writing it this way is the same as writing it this way
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`The ${this.name} moved ${distanceInMeters}m.`); }}Copy the code
private
When a member is marked private, it cannot be accessed outside the class that declared it
class Animal {
private name: string;
constructor(name: string) { this.name = name; }}new Animal("Cat").name; // Error: 'name' is private.
Copy the code
TypeScript uses a structured type system. When we compare two different types, we don’t care where they came from. If all the members’ types are compatible, we assume that they are compatible.
class Animal {
name: string
constructor(name: string) { this.name = name }
}
class Employee {
name: string
constructor(name: string) { this.name = name }
}
let animal = new Animal('Goat');
let employee = new Employee('Klaus');
animal = employee;
Copy the code
Although animal and Employee have different types, ypeScript uses a structured type system
The name attribute exists in both Animal and Employee, so typescript considers aniaml and Employee to be compatible
You can assign values to each other. However, when we compare types with private or protected members, the situation is different.
class Animal {
private name: string;
constructor(name: string) { this.name = name; }}class Rhino extends Animal {
constructor() { super("Rhino"); }}class Employee {
private name: string;
constructor(name: string) { this.name = name; }}let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino; // Success ==> Rhino is a subclass of Animal, so it has a private name
Error ===> Animal and Employee have private name
// But because the name is private, each is invisible to the other
animal = employee;
Copy the code
protected
Protected modifiers behave similar to private modifiers, except that protected members are still accessible in derived classes
class Person {
protected name: string;
constructor(name: string) { this.name = name; }}class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name)
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is The ${this.name} and I work in The ${this.department}. `; }}let howard = new Employee("Howard"."Sales");
// We can't use name outside of the Person class, but we can still access it through the instance method of the Employee class, because Employee is derived from Person.
console.log(howard.getElevatorPitch());
console.log(howard.name); // error
Copy the code
Constructors can also be marked protected. This means that the class cannot be instantiated outside the enclosing class, but it can be inherited. For instance,
class Person {
protected name: string;
protected constructor(name: string) { this.name = name; }}// Employee can inherit from Person
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is The ${this.name} and I work in The ${this.department}. `; }}let howard = new Employee("Howard"."Sales");
let john = new Person("John"); // Error: the 'Person' constructor is protected.
Copy the code
When using a subclass constructor to initialize a property of a parent class, the modifier of the subclass should be less restrictive than the modifier of the corresponding property of the parent class
class Person {
protected name: string
constructor(name: string) { this.name = name }
}
class Student extends Person {
private name: string = ' '
constructor(name: string) {
super(name) // error }}Copy the code
class Person {
protected name: string
constructor(name: string) { this.name = name }
}
class Student extends Person {
name: string = ' '
constructor(name: string) {
super(name) // success }}Copy the code
readonly
Properties can be set to read-only using the readonly keyword. Read-only attributes must be initialized at declaration time or in the constructor.
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (name: string) {
this.name = name; }}let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error => Name is read-only.
Copy the code
Parameter properties
In the example above, we had to define a read-only member name and a constructor parameter name in the Person class. This is done so that the value of name can be accessed after the Octopus constructor is executed.
This happens all the time. TypeScript defines a syntax for this case: the sugar_parameter attribute _,
The _ parameter attribute _ conveniently lets us define and initialize a member in one place.
class Animal {
/** * constructor (name: string) { * this.name = name; *} * /
// The above constructor can be abbreviated as follows
// Parameter properties are declared by adding an access qualifier (public, protected, private) to the constructor arguments
constructor(private name: string){}move(distanceInMeters: number) {
console.log(`The ${this.name} moved ${distanceInMeters}m.`); }}Copy the code
accessor
TypeScript supports getters/setters to intercept access to object members. It helps you effectively control access to object members
If a property has an accessor with only get and no set, the property is automatically inferred to be readonly
const fullNameMaxLength = 10;
class Employee {
// Private attributes usually start with an underscore
private _fullName: string = ' ';
get fullName() :string {
return this._fullName;
}
set fullName(newName: string) {
if (newName && newName.length > fullNameMaxLength) {
throw new Error("fullName has a max length of " + fullNameMaxLength);
}
this._fullName = newName; }}let employee = new Employee();
// If the set method is set, the set method is automatically called when the value is set
employee.fullName = "Bob Smith";
if (employee.fullName) {
// If the get method is set, it is automatically called when the value is set
alert(employee.fullName);
}
Copy the code
Static attributes
A class can be divided into two parts: the instance part used by instance objects of the class and the static part used by the class itself
Instance parts are properties and methods that are initialized only when the class is instantiated
The static parts are those properties and methods that exist on the class itself rather than on instances of the class
In typeScript, we can use the static keyword to define static properties and methods
class Animal {
name: string
static breed: string
constructor(name: string) { this.name = name }
eat() { console.log('eatting... ')}static play() { console.log('playing... ')}}const animal = new Animal('Cat')
// Access instance methods and instance properties
console.log(animal.name)
animal.eat()
// Access static methods and static properties
console.log(Animal.breed)
Animal.play()
Copy the code
An abstract class
- Abstract classes are used as base classes for other derived classes
- An abstract class cannot be instantiated directly because abstract methods in an abstract class contain no concrete implementation and must be implemented in a derived class
- Unlike an interface, an abstract class can contain the implementation details of its members (other functions in an abstract class can contain concrete implementations).
abstract
Keywords are used to define abstract classes and to define abstract methods within abstract classes- Abstract methods can contain access modifiers
absrtact
It can be used on properties, but it doesn’t make much sense to do so
abstract class Animal {
abstract makeSound(): void; // Define abstract methods
move(): void {
console.log("roaming the earth..."); }}Copy the code
Class as an interface
Classes can create types, so you can use classes where interfaces are allowed.
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1.y: 2.z: 3};
Copy the code