类的定义
实例上的属性需要先声明在使用,构造函数中的参数可以使用可选参数和剩余参数
class Person{
// 需要赋值,要不会报错。或者可以使用非空断言
name:string='';
//name!:string;
say():void{
console.log(this.name);
}
}
let p1 = new Person();
p1.name = 'fang';
p1.say();
编译完成后的代码如下
var Person = /** @class */ (function () {
function Person() {
this.name = '';
}
Person.prototype.say = function () {
console.log(this.name);
};
return Person;
}());
var p1 = new Person();
p1.name = 'fang';
p1.say();
class Pointer {
x!:number // 实例上的属性必须先声明
y!:number
constructor(x, y) { // 构造函数也是函数
this.x = x
this.y = y
}
}
const point = new Pointer(1, 2)
console.log(point);
存取器
- 在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为
- 构造函数
- 主要用于初始化类的成员变量属性
- 类的对象创建时自动调用执行
- 没有返回值
编译后的结果如下:class Person{
myName:string;
constructor(myName: string) {
this.myName = myName;
}
get name():string {
return this.myName;
}
set name(val) {
this.myName = val;
}
}
let p = new Person('f');
console.log(p.name)
var Person = /** @class */ (function () {
function Person(myName) {
this.myName = myName;
}
Object.defineProperty(Person.prototype, "name", {
get: function () {
return this.myName;
},
set: function (val) {
this.myName = val;
},
enumerable: false,
configurable: true
});
return Person;
}());
var p = new Person('f');
console.log(p.name);
绝对不能像下面这样
class Person{
name:string;
constructor(name: string) {
this.name = name;
}
get name() {
return this.name;
}
set name(val) {
this.name = name;
}
}
let p1 = new Person('f');
p1.name = 'fang';
参数属性
readonly
- readonly修饰的变量只能在构造函数中初始化
- 在 TypeScript 中,const 是常量标志符,其值不能被重新分配
- TypeScript 的类型系统同样也允许将 interface、type、 class 上的属性标识为 readonly
readonly 实际上只是在编译阶段进行代码检查。而 const 则会在运行时检查(在支持 const 语法的 JavaScript 运行时环境中)
继承
子类继承父类后子类的实例就拥有了父类中的属性和方法,可以增强代码的可复用性
- 将子类公用的方法抽象出来放在父类中,自己的特殊逻辑放在子类中重写父类的逻辑
-
类里面的修饰符
类里面的修饰符有三种 public、protected、private。
public:类里面 子类 其它任何地方外边都可以访问
protected:类里面 子类 都可以访问,其它任何地方不能访问
private:类里面可以访问, 子类和其它任何地方都不可以访问 ```javascript class Parent { public name: string; protected age: number; private money: number; constructor(name: string, age:number, money:number) { this.name = name; this.age = age; this.money = money; } } class Child extends Parent { constructor(name: string, age: number, money: number) { super(name, age, money); } say() { console.log(this.name); console.log(this.age);// // semantic error TS2341: Property ‘money’ is private and only accessible within class ‘Parent’. // console.log(this.money); } }
const child = new Child(‘f’, 18, 999999999); console.log(child.name);
// semantic error TS2445: Property ‘age’ is protected and only accessible within class ‘Parent’ and its subclasses. // console.log(child.age);
// semantic error TS2341: Property ‘money’ is private and only accessible within class ‘Parent’. // console.log(child.money);
<a name="Rnken"></a>
# 静态属、静态方法
```typescript
// 静态属性和静态方法
class Father {
static className='Father';
static getClassName() {
return Father.className;
}
public name: string;
constructor(name:string) {//构造函数
this.name=name;
}
}
console.log(Father.className); // 'Father'
console.log(Father.getClassName()); // 'Father'
编译后的结果如下:
(function () {
'use strict';
// 静态属性和静态方法
var Father = (function () {
function Father(name) {
this.name = name;
}
Father.getClassName = function () {
return Father.className;
};
Father.className = 'Father';
return Father;
}());
console.log(Father.className); // 'Father'
console.log(Father.getClassName()); // 'Father'
})();
可以看到静态属性和静态方法都挂载到了类上。
在子类中调用父类的属性和方法,我们可以使用 super:
class Child extends Father {
getName() {
super.FatherName;
}
}
装饰器
- 装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为
- 常见的装饰器有类装饰器、属性装饰器、方法装饰器和参数装饰器
- 装饰器的写法分为普通装饰器和装饰器工厂
在使用装饰器前我们需要先将 tsconfig.json 文件中的 "experimentalDecorators": true
类装饰器
类装饰器在类声明之前声明,用来监视、修改或替换类定义
//当装饰器作为修饰类的时候,会把构造器传递进去
function addNameEat(constructor: Function) {
console.log(constructor)
constructor.prototype.name = "f";
constructor.prototype.eat = function () {
console.log("eat");
};
}
@addNameEat
class Person {
name!: string;
eat!: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name); // 'f'
p.eat(); // 'eat'
还可以使用装饰器工厂:
function addNameEatFactory(name:string) {
return function (constructor: Function) {
constructor.prototype.name = name;
constructor.prototype.eat = function () {
console.log("eat");
};
};
}
@addNameEatFactory('f')
class Person {
name!: string;
eat!: Function;
constructor() {}}
let p: Person = new Person();
console.log(p.name);
p.eat();
还可以替换类,不过替换的类要与原类结构相同
function enhancer(constructor: Function) {
return class {
name: string = "f";
eat() {
console.log("吃饭饭");
}
};
}
@enhancer
class Person {
name!: string;
eat!: Function;
constructor() {}}
let p: Person = new Person();
console.log(p.name);
p.eat();
命名空间的
namespace a {
let b = 1;
console.log(b)
}
命名空间的本质
(function () {
'use strict';
(function (a) {
var b = 1;
console.log(b);
})();
})();
属性装饰器
- 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
- 属性装饰器用来装饰属性
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是属性的名称
如果装饰的是实例属性的话 target 是构造函数的原型:
function upperCase(target: any, key: string) {
console.log(target, key); // Person name
let val = target[key];
const getter = () => val;
const setter = (newVal: string) => val = newVal.toUpperCase();
if (delete target[key]) {
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
class Person {
@upperCase name: string = 'f';
}
const p = new Person;
console.log(p.name);
如果装饰的是静态属性的话,target 是构造函数本身:
function staticPropDecorator(target: any, key: string) {
console.log(target, key); // [Function: Person] age
target[key] = target[key] + 10 // 18
}
class Person {
@staticPropDecorator
static age: number = 8;
}
console.log(Person.age);
方法装饰器用来装饰方法
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是方法的名称
- 第三个参数是方法描述符 ```typescript //修饰实例方法 function noEnumerable(target: any, property: string, descriptor: PropertyDescriptor) { console.log(target); // Person { getName: [Function] } console.log(property); // getName console.log(descriptor); // {value: [Function],writable: true, enumerable: true, configurable: true}
descriptor.enumerable = false; } class Person { name: string = ‘f’ @noEnumerable getName() {
console.log(this.name);
} } let p: Person = new Person(); for (let attr in p) { console.log(attr); // 只打印出了 name 属性,并没有打印出 getName } p.getName(); // 可以正常调用 getName 方法 ```
参数装饰器
会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元数据
- 第1个参数对于静态成员是类的构造函数,对于实例成员是类的原型对象
- 第2个参数的名称
- 第3个参数在函数列表中的索引 ```typescript function addAge(target: any, methodName: string, paramsIndex: number) { console.log(target); // Person { setInfo: [Function] } console.log(methodName); // setInfo console.log(paramsIndex); // 0
target.age = 18; } class Person { name!: string; age!: number; setInfo(@addAge name:string) { this.name = name; console.log(this.name, this.age); // ‘f’ 18 } } let p = new Person(); p.setInfo(‘f’);
console.log(p) // Person { name: ‘f’ }
<a name="seAgH"></a>
## 装饰器执行的顺序
- 有多个参数装饰器时:从最后一个参数依次向前执行
- 方法和方法参数中参数装饰器先执行。
- 类装饰器总是最后执行
- 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行
- 类比React组件的componentDidMount 先上后下、先内后外
```typescript
function Class1Decorator() {
return function (target: any) {
console.log("类1装饰器");
}
}
function Class2Decorator() {
return function (target: any) {
console.log("类2装饰器");
}
}
function MethodDecorator() {
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
console.log("方法装饰器");
}
}
function Param1Decorator() {
return function (target: any, methodName: string, paramIndex: number) {
console.log("参数1装饰器");
}
}
function Param2Decorator() {
return function (target: any, methodName: string, paramIndex: number) {
console.log("参数2装饰器");
}
}
function PropertyDecorator(name: string) {
return function (target: any, propertyName: string) {
console.log(name + "属性装饰器");
}
}
@Class1Decorator()
@Class2Decorator()
class Person {
@PropertyDecorator('name')
name: string = 'f';
@PropertyDecorator('age')
age: number = 10;
@MethodDecorator()
greet(@Param1Decorator() p1: string, @Param2Decorator() p2: string) { }
}
打印结果如下:
name属性装饰器
age属性装饰器
参数2装饰器
参数1装饰器
方法装饰器
类2装饰器
类1装饰器
抽象类
- 抽象描述一种抽象的概念,无法被实例化,只能被继承
- 无法创建抽象类的实例
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现 ```typescript abstract class Person { name: string = ‘f’; abstract speak():void }
class Man extends Person { speak(): void { console.log(this.name); } }
const man = new Man; console.log(man.name)
| 访问控制修饰符 | private protected public |
| --- | --- |
| 只读属性 | readonly |
| 静态属性 | static |
| 抽象类、抽象方法 | abstract |
<a name="JYq7c"></a>
# 抽象方法
- 抽象类和方法不包含具体实现,必须在子类中实现
- 抽象方法只能出现在抽象类中
- 子类可以对抽象类进行不同的实现
```typescript
abstract class Person {
abstract speak(): void;
}
class Man extends Person {
speak(): void {
console.log('man')
}
}
class Woman extends Person {
speak(): void {
console.log('woman')
}
}
const man = new Man;
const woman = new Woman;
man.speak(); // 'man'
woman.speak(); // 'woman'
重写和重载
- 重写是指子类重写继承自父类中的方法 ```typescript class Parent { speak() { console.log(‘parent’) } } class Child extends Parent { speak() { console.log(‘child’) } }
const child = new Child; child.speak(); // ‘child’
- 重载是指为同一个函数提供多个类型定义
```typescript
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
console.log(add(1, 2))
console.log(add('hello', 'world'))
继承和多态
- 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为 ```typescript class Person { // 多态 同一个方法,不同的子类有不同的实现 speak() { } } class Man extends Person { speak() { console.log(‘man’) } }
class Woman extends Person { speak() { console.log(‘woman’) } }
const man = new Man; const woman = new Woman; man.speak(); // ‘man’ woman.speak(); // ‘woman’ ```