ES6 类
ES6(ECMAScript 2015)引入了类的概念,为 JavaScript 增加了面向对象编程的能力。ES6 中的类是一种语法糖,本质上仍然是基于原型的继承。使用类可以定义构造函数、实例方法和静态方法,并且支持继承和类之间的关系。
ES6 类的常见特性
以下是一些 ES6 类的常见特性:
1. 构造函数
类中的构造函数通过 constructor
关键字定义,并且在创建实例时被调用。
当使用 ES6 类创建对象时,构造函数是在实例化过程中自动调用的方法。构造函数使用 constructor
关键字定义在类内部,它负责初始化对象的属性和状态。
以下是一个使用 ES6 的构造函数的示例:
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } } const person1 = new Person('Alice', 28); person1.greet(); // 输出:Hello, my name is Alice and I am 28 years old.
在上面的例子中,我们创建了一个名为 Person
的类。构造函数 constructor
接受两个参数 name
和 age
,并将它们分别赋值给实例属性 this.name
和 this.age
。greet
方法用于打印出个人信息。
通过使用 new
关键字,我们可以实例化该类并传入相应的参数。在实例化过程中,构造函数会被自动调用,初始化属性。然后,我们可以调用 greet
方法来打印出个人信息。
值得注意的是,ES6 类中的构造函数只能有一个,且使用 super
关键字来调用父类的构造函数(如果有继承关系)。构造函数可以执行各种操作,例如初始化数据、调用其他方法或执行其他逻辑,以确保实例在创建时处于正确的状态。
2. 实例方法
类中的实例方法不需要使用关键字 function
声明,直接在类体内声明即可。实例方法默认会绑定到实例对象。
在 ES6 类中,实例方法是定义在类的原型上的方法,可以被类的实例调用。以下是一个使用 ES6 的实例方法的示例:
class Circle { constructor(radius) { this.radius = radius; } calculateArea() { return Math.PI * this.radius * this.radius; } calculateCircumference() { return 2 * Math.PI * this.radius; } } const circle1 = new Circle(5); console.log(circle1.calculateArea()); // 输出:78.53981633974483 console.log(circle1.calculateCircumference()); // 输出:31.41592653589793
在上面的例子中,我们创建了一个名为 Circle
的类。该类有一个构造函数,接受一个参数 radius
并将其赋值给实例属性 this.radius
。
然后,我们定义了两个实例方法 calculateArea
和 calculateCircumference
。calculateArea
方法用于计算圆的面积,calculateCircumference
方法用于计算圆的周长。这两个方法都可以通过类的实例进行调用。
使用 new
关键字我们实例化了 Circle
类,并传入半径为 5。然后我们分别调用 calculateArea
和 calculateCircumference
方法,并打印出结果。
ES6 类中的实例方法是绑定到类的原型上的,这意味着每个类的实例都共享同一个实例方法的实现,从而节省了内存空间。同时,实例方法可以访问类的实例属性和其他实例方法,允许我们在方法内部处理实例的数据和状态。
3. 静态方法
类中的静态方法使用 static
关键字声明,静态方法与实例无关,可以直接通过类来调用。
在 ES6 类中,静态方法是定义在类本身上的方法,而不是类的实例。可以通过类名直接调用静态方法,而不需要创建类的实例。以下是一个使用 ES6 的静态方法的示例:
class MathUtils { static add(a, b) { return a + b; } static subtract(a, b) { return a - b; } } console.log(MathUtils.add(5, 3)); // 输出:8 console.log(MathUtils.subtract(10, 4)); // 输出:6
在上面的例子中,我们创建了一个名为 MathUtils
的类。该类定义了两个静态方法 add
和 subtract
。这两个方法都可以直接通过类名进行调用,而不需要创建类的实例。
使用类名 MathUtils
来调用静态方法,并传入相应的参数。然后我们打印出结果。
ES6 类中的静态方法是绑定到类本身的,而不是绑定到类的实例。因此,它们不能访问类的实例属性或其他实例方法。静态方法通常用于执行与类相关的操作,例如辅助函数、工具函数或对象的创建和管理。
可以通过在方法前添加 static
关键字来定义静态方法。静态方法可以通过类名直接调用,而无需实例化对象。这使得静态方法具有更高的灵活性和代码组织性,可以在不创建实例的情况下执行特定的功能。
4. 继承
通过 extends
关键字可以实现类的继承,子类可以继承父类的属性和方法,并可以覆盖或扩展父类的功能。
在 ES6 中,我们可以使用 extends
关键字实现类的继承。通过继承,一个子类可以继承父类的属性和方法,同时可以添加新的属性和方法。以下是一个使用 ES6 实现继承的代码示例:
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Dog extends Animal { constructor(name, breed) { super(name); this.breed = breed; } bark() { console.log(`${this.name} barks loudly.`); } } const myDog = new Dog('Max', 'Labrador'); myDog.speak(); // 输出:Max makes a sound. myDog.bark(); // 输出:Max barks loudly. console.log(myDog.breed); // 输出:Labrador
在上面的例子中,我们创建了一个名为 Animal
的父类。该类有一个构造函数,接受一个参数 name
并将其赋值给实例属性 this.name
。还定义了一个方法 speak
,用于输出动物发出声音。
然后,我们创建了一个名为 Dog
的子类,使用 extends
关键字来继承父类 Animal
。子类 Dog
有一个构造函数,接受两个参数 name
和 breed
。在子类的构造函数中,我们使用 super
关键字调用父类的构造函数,并传递 name
参数。然后,我们将 breed
参数赋值给子类特有的实例属性 this.breed
。
子类 Dog
还定义了一个新的方法 bark
,用于输出狗的吠声。
通过创建 Dog
类的实例 myDog
,我们可以调用继承自父类的方法 speak
和子类自己的方法 bark
。还可以访问子类特有的属性 breed
。
继承允许我们在子类中复用父类的代码和功能,并在子类中添加更多特定的属性和方法。这提高了代码的可重用性和可维护性,并支持面向对象编程的概念,如封装、继承和多态。
TypeScript 类
TypeScript 是一个由 Microsoft 开发的开源编程语言,它是 JavaScript 的一个超集,增加了静态类型检查和面向对象编程的特性。TypeScript 中的类和 ES6 类有相似之处,但又具有更强的类型系统和其他功能。
TypeScript 类的特性
以下是一些 TypeScript 类的特性:
1. 类型注解
TypeScript 强调类型的静态检查,可以在类的属性、方法和参数上添加类型注解,以约束数据类型。
TypeScript 是 JavaScript 的一个超集,它提供了类型注解的功能来静态检查代码。通过类型注解,我们可以为变量、函数参数、函数返回值等添加类型信息,从而使编译器能够检查代码中的类型错误,并在开发过程中提供更好的代码提示和自动补全。
以下是一个使用 TypeScript 的类型注解的代码示例:
function greet(name: string): void { console.log(`Hello, ${name}!`); } let username: string = "John"; greet(username); // 输出:Hello, John!
在上面的例子中,我们定义了一个名为 greet
的函数。函数接受一个参数 name
,并且我们使用 : string
进行类型注解来指定该参数的类型为字符串。
同时,我们还为函数的返回值 void
添加了类型注解,表示该函数没有返回值。
在函数调用时,我们创建一个名为 username
的变量,并将其类型注解为字符串。然后,我们将 username
作为参数传递给 greet
函数。
TypeScript 编译器将根据类型注解进行代码检查,以确保传递给函数的参数类型正确,并且函数的返回值与类型注解一致。
类型注解提供了一种强大的工具,可以增强代码的可读性和可维护性,并提供更好的开发体验。它可以帮助我们在编译阶段发现潜在的类型错误,并减少在运行时出现的错误。此外,类型注解还提供了更好的代码提示和自动补全功能,以提高开发效率。
2. 访问修饰符
TypeScript 提供了 public
、protected
和 private
等访问修饰符,用于控制类成员的访问权限。
TypeScript 提供了访问修饰符来控制类的属性和方法的访问权限。有三种主要的访问修饰符:public
、protected
和 private
。
下面是一个使用 TypeScript 访问修饰符的示例代码:
class Person { public name: string; protected age: number; private phoneNumber: string; constructor(name: string, age: number, phoneNumber: string) { this.name = name; this.age = age; this.phoneNumber = phoneNumber; } public introduce(): void { console.log(`My name is ${this.name}. I am ${this.age} years old.`); this.privateMethod(); } protected protectedMethod(): void { console.log("This is a protected method."); } private privateMethod(): void { console.log("This is a private method."); } } class Employee extends Person { public position: string; constructor(name: string, age: number, phoneNumber: string, position: string) { super(name, age, phoneNumber); this.position = position; } public getPhoneNumber(): void { console.log(`${this.name}'s phone number is ${this.phoneNumber}.`); this.protectedMethod(); } } const john = new Person("John", 30, "123456789"); console.log(john.name); // 公有属性,可以访问 // console.log(john.age); // 受保护属性,不能在类外部访问 // console.log(john.phoneNumber); // 私有属性,不能在类外部访问 john.introduce(); // 公有方法,可以访问 const employee = new Employee("Bob", 25, "987654321", "Manager"); // console.log(employee.name); // 公有属性,可以访问 // console.log(employee.age); // 受保护属性,不能在类外部访问 // console.log(employee.phoneNumber); // 私有属性,不能在类外部访问 employee.introduce(); // 公有方法,可以访问 employee.getPhoneNumber(); // 子类可以访问受保护方法
在上面的示例中,我们创建了一个名为 Person
的基类,并定义了三个属性:name
、age
和 phoneNumber
。name
是公有属性,可以在类外部访问;age
是受保护属性,只能在类内部及其子类中访问;phoneNumber
是私有属性,只能在类内部访问。
我们还定义了两个方法:introduce
、protectedMethod
和 privateMethod
。introduce
是公有方法,可以在类外部调用;protectedMethod
是受保护方法,只能在类内部及其子类中调用;privateMethod
是私有方法,只能在类内部调用。
然后,我们创建了一个名为 Employee
的子类,继承自 Person
。子类拥有 position
属性,并在构造函数中通过 super
关键字调用父类的构造函数来初始化继承的属性。
通过创建 Person
和 Employee
的实例,我们可以看到不同访问修饰符的效果。只有公有属性和方法可以从类外部访问,受保护属性和方法只能在类内部及其子类中访问,而私有属性和方法只能在类内部访问。
访问修饰符允许我们控制类的成员的可见性和可访问性,并提供了一种封装数据和行为的方式,增强了代码的安全性和可维护性。
3. 类型推断
TypeScript 可以根据赋值表达式的右侧推断出变量的类型,减少重复的类型注解。
TypeScript 提供了类型推断的功能,能够根据赋值表达式的右侧值自动推断变量的类型。下面是一个使用 TypeScript 类型推断的示例代码:
let name = "Alice"; // 类型推断为 string let age = 30; // 类型推断为 number let isStudent = true; // 类型推断为 boolean let fruits = ["apple", "banana", "orange"]; // 类型推断为 string[] let numbers = [1, 2, 3]; // 类型推断为 number[] let matrix = [[1, 2], [3, 4]]; // 类型推断为 number[][] let person = { name: "Bob", age: 25, }; // 类型推断为 { name: string, age: number } function add(a: number, b: number) { return a + b; } // 函数参数和返回值的类型推断为 number class Animal { name: string; constructor(name: string) { this.name = name; } } // 类的属性和构造函数参数的类型推断为 string let animal = new Animal("Dog"); // 类的实例类型推断为 Animal
在上述示例中,我们声明了一些变量并初始化它们,TypeScript 根据赋值的值来推断变量的类型。例如,通过字符串赋值给变量 name
,TypeScript 推断出 name
的类型为 string
。
类似地,数组的元素类型也可以通过初始化的值进行推断。在示例中,fruits
的类型被推断为 string[]
,即字符串数组。
在函数定义中,参数 a
和 b
的类型被推断为 number
,因为我们在函数体内使用了加法操作,而返回值的类型也被推断为 number
。
对于类,属性和构造函数参数的类型也可以通过初始化来推断。在示例中,Animal
类的属性 name
和构造函数参数 name
都被推断为 string
类型。
最后,当我们创建 Animal
类的实例 animal
时,TypeScript 推断出它的类型为 Animal
。
类型推断是 TypeScript 的一个强大功能,能够减少显式类型注解的冗余,并帮助开发人员更轻松地编写类型安全的代码。
4. 接口实现
TypeScript 支持类实现接口,通过 implements
关键字来强制类遵循接口的契约。
在 TypeScript 中,接口(Interfaces)用于定义对象的结构和类型。一个类可以通过实现(implements)一个接口来强制遵循该接口所定义的结构。下面是一个使用 TypeScript 接口实现的示例代码:
interface Shape { getArea(): number; } class Circle implements Shape { radius: number; constructor(radius: number) { this.radius = radius; } getArea(): number { return Math.PI * this.radius ** 2; } } class Rectangle implements Shape { width: number; height: number; constructor(width: number, height: number) { this.width = width; this.height = height; } getArea(): number { return this.width * this.height; } } const circle = new Circle(5); console.log(circle.getArea()); // 输出: 78.53981633974483 const rectangle = new Rectangle(4, 6); console.log(rectangle.getArea()); // 输出: 24
在上述示例中,我们定义了一个名为 Shape
的接口,它有一个名为 getArea
的方法,返回类型为 number
。
然后我们创建了两个类 Circle
和 Rectangle
,它们分别实现了 Shape
接口。这意味着这两个类必须实现 Shape
接口中定义的方法 getArea
。
Circle
类有一个属性 radius
,并在构造函数中初始化。它实现了 getArea
方法来计算圆的面积。
Rectangle
类有两个属性 width
和 height
,并在构造函数中初始化。它同样实现了 getArea
方法来计算矩形的面积。
通过创建 Circle
和 Rectangle
类的实例,并调用它们的 getArea
方法,我们可以得到圆和矩形的面积。
接口的使用使得我们能够在 TypeScript 中定义和强制对象的结构,并确保类遵循指定的接口约束。这提高了代码的可读性和可维护性,并加强了类型检查和类型安全性。
总结来说,ES6 类是 JavaScript 中基于原型的面向对象编程的语法糖,而 TypeScript 类在此基础上增加了强类型检查和其他面向对象编程的特性,使得代码更具可读性、可维护性和可靠性。