# typescript中的类
# 类的定义
class Person{
name:string, // 类中的属性 前面省略了public关键字
// 构造函数 constructor
// 主要用于初始化类的成员变量属性
// 类的对象创建时自动调用执行
// 没有返回值
constructor(name:string){
this.name = name;
}
getName():void{
console.log(this.name)
}
setName(name:string):void{
this.name = name
}
}
var p = new Person('张某某')
p.getName() // 张某某
p.setName('李四')
p.getName(); // 李四
# 存取器
在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为
class User {
myname:string;
constructor(myname: string) {
this.myname = myname;
}
get name() {
return this.myname;
}
set name(value) {
this.myname = value;
}
}
let user = new User('jc-sir');
user.name = 'juncai';
console.log(user.name);
"use strict";
var User = /** @class */ (function () {
function User(myname) {
this.myname = myname;
}
Object.defineProperty(User.prototype, "name", {
get: function () {
return this.myname;
},
set: function (value) {
this.myname = value;
},
enumerable: true,
configurable: true
});
return User;
}());
var user = new User('jc-sir');
user.name = 'juncai';
console.log(user.name);
# 参数属性
class User {
constructor(public name: string) {}
get name() {
return this.name;
}
set name(value) {
this.name = value;
}
}
// 等同于
class User {
name;
constructor(name: string) {this.name=name}
}
let user = new User('jc');
console.log(user.name);
# ts中实现继承
# 最基本的继承
示例来自官网
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
// extends关键字实现继承
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark(); // Woof! Woof!
dog.move(10); // Animal moved 10m
# 稍复杂点的继承
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
eat(food:string = '粮食'):void{
console.log(`${this.name}吃${food}`)
}
}
class Dog extends Animal {
constructor(name: string) { super(name); }
eat(food) {
super.eat(food);
}
}
class Cat extends Animal {
constructor(name: string) { super(name); }
eat(food) {
super.eat(food);
}
}
let dog = new Dog("小黄");
let cat: Animal = new Cat("英短");
dog.eat('狗粮'); // 小黄吃狗粮
cat.eat('猫粮'); // 英短吃猫粮
提示
- 与前一个例子的不同点是,派生类包含了一个构造函数,它必须调用 super(),它会执行基类的构造函数。
- 在构造函数里访问 this的属性之前,我们 一定要调用 super()。
- 这个是TypeScript强制执行的一条重要规则。
# 类中的修饰符
类型 | 类型说明 | 可访问性 |
---|---|---|
public | 公有类型 | 在类里面、子类、类外部(实例)都以访问 |
protected | 保护类型 | 在类里面、子类可以访问 类外部(实例)无法访问 |
private | 私有类型 | 只能在类里面可以访问 |
不指定类型默认为public
class Person{
public name:string;
protected sex:string = '男';
private age:number = 19;
constructor(name:string){
this.name = name;
}
getPrototype():void{
console.log(`${this.name}-${this.sex}-${this.age}`)
}
}
class Man extends Person{
constructor(name:string){
super(name)
}
testType():void{
console.log(`${this.name}`)
console.log(`${this.sex}`)
console.log(`${this.age}`)
// Property 'age' is private and only accessible within class 'Person'.
}
}
var p = new Person('小王');
p.getPrototype() // 小王-男-19
console.log(p.name) // 小王
// protected 无法在外部访问
console.log(p.sex)
// Property 'sex' is protected and only accessible within class 'Person' and its subclasses.
// 类的外部无法访问私有属性
console.log(p.age)
// Property 'age' is private and only accessible within class 'Person'.
var man = new Man('小李');
man.getPrototype() // 小李-男-19
console.log(man.name) // 小李
# 静态属性与静态方法
创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。
class AnimalClass{
name:string;
sex:string='男'
static staticProperty:string ='静态属性'
constructor(name:string){
this.name=name;
}
run(){
console.log('实例方法')
}
// 静态方法 static修饰
static staticFun(params:string):void{
console.log(params);
// 静态方法里面无法直接调用类里面的属性
console.log(`${params}--${this.sex}`); // 我是静态方法--undefined
console.log(`${params}--${this.staticProperty}`);
}
}
// 静态方法
AnimalClass.eat = function():void{
console.log('eat Function')
}
// 静态方法调用 类名+方法名
AnimalClass.eat();
AnimalClass.staticFun('我是静态方法');
// 获取静态属性
console.log(AnimalClass.staticProperty);
# 装饰器
# 类装饰器
类装饰器在类声明之前声明,用来监视、修改或替换类定义
namespace a {
//当装饰器作为修饰类的时候,会把构造器传递进去
function addNameEat(constructor: Function) {
constructor.prototype.name = "jc-sir";
constructor.prototype.eat = function () {
console.log("eat");
};
}
@addNameEat
class Person {
name!: string;
eat!: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name);
p.eat();
}
namespace b {
//还可以使用装饰器工厂
function addNameEatFactory(name:string) {
return function (constructor: Function) {
constructor.prototype.name = name;
constructor.prototype.eat = function () {
console.log("eat");
};
};
}
@addNameEatFactory('jc-sir')
class Person {
name!: string;
eat!: Function;
constructor() {}}
let p: Person = new Person();
console.log(p.name);
p.eat();
}
namespace c {
//还可以替换类,不过替换的类要与原类结构相同
function enhancer(constructor: Function) {
return class {
name: string = "jc-sir";
eat() {
console.log("吃饭");
}
};
}
@enhancer
class Person {
name!: string;
eat!: Function;
constructor() {}}
let p: Person = new Person();
console.log(p.name);
p.eat();
}
# 属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
属性装饰器用来装饰属性
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是属性的名称
方法装饰器用来装饰方法
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是方法的名称
- 第三个参数是方法描述符
namespace d {
//修饰实例属性
function upperCase(target: any, propertyKey: string) {
let value = target[propertyKey];
const getter = function () {
return value;
}
// 用来替换的setter
const setter = function (newVal: string) {
value = newVal.toUpperCase()
};
// 替换属性,先删除原先的属性,再重新定义属性
if (delete target[propertyKey]) {
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
//修饰实例方法
function noEnumerable(target: any, property: string, descriptor: PropertyDescriptor) {
console.log('target.getName', target.getName);
console.log('target.getAge', target.getAge);
descriptor.enumerable = true;
}
//重写方法
function toNumber(target: any, methodName: string, descriptor: PropertyDescriptor) {
let oldMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
args = args.map(item => parseFloat(item));
return oldMethod.apply(this, args);
}
}
class Person {
@upperCase
name: string = 'zzz'
public static age: number = 10
constructor() { }
@noEnumerable
getName() {
console.log(this.name);
}
@toNumber
sum(...args: any[]) {
return args.reduce((accu: number, item: number) => accu + item, 0);
}
}
let p: Person = new Person();
for (let attr in p) {
console.log('attr=', attr);
}
p.name = 'xxx';
p.getName();
console.log(p.sum("1", "2", "3"));
}
# 参数装饰器
会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元数据
- 第1个参数对于静态成员是类的构造函数,对于实例成员是类的原型对象
- 第2个参数的名称
- 第3个参数在函数列表中的索引
namespace d {
interface Person {
age: number;
}
function addAge(target: any, methodName: string, paramsIndex: number) {
console.log(target);
console.log(methodName);
console.log(paramsIndex);
target.age = 10;
}
class Person {
login(username: string, @addAge password: string) {
console.log(this.age, username, password);
}
}
let p = new Person();
p.login('jc-sir', '123456')
}
# 装饰器执行顺序
- 有多个参数装饰器时:从最后一个参数依次向前执行
- 方法和方法参数中参数装饰器先执行。
- 类装饰器总是最后执行
- 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行
- 类比React组件的componentDidMount 先上后下、先内后外
namespace e {
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 = 'jc-sir';
@PropertyDecorator('age')
age: number = 10;
@MethodDecorator()
greet(@Param1Decorator() p1: string, @Param2Decorator() p2: string) { }
}
}
/**
name属性装饰器
age属性装饰器
参数2装饰器
参数1装饰器
方法装饰器
类2装饰器
类1装饰器
*/
# 抽象类
- 抽象描述一种抽象的概念,无法被实例化,只能被继承
- 无法创建抽象类的实例
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
abstract class Animal {
name!:string;
abstract speak():void;
}
class Cat extends Animal{
speak(){
console.log('喵~');
}
}
let animal = new Animal(); // Cannot create an instance of an abstract class
animal.speak();
let cat = new Cat();
cat.speak();
# 抽象方法
- 抽象类和方法不包含具体实现,必须在子类中实现
- 抽象方法只能出现在抽象类中
- 子类可以对抽象类进行不同的实现
abstract class Animal{
abstract speak():void;
}
class Dog extends Animal{
speak(){
console.log('小狗汪汪汪');
}
}
class Cat extends Animal{
speak(){
console.log('小猫喵喵喵');
}
}
let dog=new Dog();
let cat=new Cat();
dog.speak(); // 小狗汪汪汪
cat.speak(); // 小猫喵喵喵
# 重写(override) vs 重载(overload)
- 重写是指子类重写继承自父类中的方法
- 重载是指为同一个函数提供多个类型定义
class Animal{
speak(word:string):string{
return '动作叫:'+word;
}
}
class Cat extends Animal{
// override
speak(word:string):string{
return '猫叫:'+word;
}
}
let cat = new Cat();
console.log(cat.speak('hello'));
//--------------------------------------------
// overload
function double(val:number):number
function double(val:string):string
function double(val:any):any{
if(typeof val == 'number'){
return val *2;
}
return val + val;
}
let r = double(1);
console.log(r);
# 继承vs多态
- 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为
class Animal{
name:string;
constructor(name:string){
this.name = name;
}
eat(){}
}
class Dog extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name + '吃狗粮'
}
}
class Cat extends Animal{
constructor(name:string){
super(name)
}
eat(){
return this.name + '吃猫粮'
}
}