TS(四)类

本文目录:

  • 1.创建对象实例
  • 2.继承
  • 3.静态方法和属性
  • 4.访问修饰符
  • 5.readonly
  • 6.抽象类

1.创建对象实例

在js中,生成实例对象的传统方法是通过构造函数
我们首先通过传统的构造函数和原型对象的方法来看一下对象实例的创建

function Greeter(message) {
  this.msg = message;
}
Greeter.prototype.greeter = function() {
  return 'hello: ' + this.msg;
};
let m1 = new Greeter('传统方式创建对象实例');
console.log(m1.msg);
console.log(m1.greeter());

接下来我们再通过类class的方式 生成一个对象实例
我们在ES6的时候,实例属性都是定义在constructor()方法里面, 在ES7里 我们可以直接将这个属性定义在类的最顶层,其它都不变,去掉this,msg和flag就是我们定义在最顶层的实例属性
constructor(构造函数)方法是类的默认方法
一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加

class Greeter {
  msg: string;
  flag: boolean = false;
  // 关于构造函数:
  constructor(message: string) {
    this.msg = message;
  }
  greeter() {
    console.log('这个是在constructor构造函数外部定义实例属性:', this.flag);
    return 'hello: ' + this.msg;
  }
}
let g2 = new Greeter('通过类创建的对象实例');
console.log(g2.msg);
console.log(g2.greeter());

接下来我们来分析一些,ES6新增的class语法糖,和构造函数的一些关系
类class的类型 本质上是一个函数; 类本身就指向自己的构造函数

console.log(typeof Greeter);
console.log(Greeter === Greeter.prototype.constructor);
console.log(g2.greeter === Greeter.prototype.greeter);

通过上面在这个代码我们也可以发现,new类的时候就相当于new构造函数
调用类上面的方法就是调用原型上的方法
在类的实例上面调用方法,其实就是调用原型上的方法

2.继承

  1. 使用继承来扩展现有的类,是面向对象的三大特性之一(封装,继承,多态)
  2. 基类,父类,超类是指被继承的类,派生类,子类是指继承于基类的类
  3. ts中类继承类似于传统面向对象编程语言中的继承体系 ,使用extends关键字继承,类中this表示此当前对象本身,super表父类对象。子类构造函数中第一行代码调用父类构造函数完成初始化,然后再进行子类的进一步初始化。子类中可以访问父类(public、protected)的成员属性、方法
  4. 派生类包含了constructor; ts 规定只要派生类里面自定义了一个constructor函数就必须在使用this前,调用一下super方法
  • ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this));ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this
  • 因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象

子类方法名和父类相同表示重写父类方法
业务需求:我们现在有两个类,一个动物类,一个狗类, 狗也是动物,所以会继承动物类的一些属性和方法

class Animal {
  name: string;
  constructor(param: string) {
    this.name = param;
  }
  move(distance: number = 0) {
    console.log(`${this.name} 移动了 ${distance}m.`);
  }
}
class Dog extends Animal {
   bark() {
      console.log('狗叫!');
   }
}  
const dog = new Dog('阿黄');
console.log(dog.name);
dog.bark();
dog.move(10);
dog.bark();

上面这个例子中 动物类是基类,也可以叫父类; 狗是子类也可以叫派生类, 继承自动物类,可以使用父类的任何方法和属性
我们将上面的代码稍微做一下修改

class Dog extends Animal {
  dogName2: string;
  constructor(name: string) {
   // 派生类包含了一个构造函数,就必须首先调用super()方法,会调用基类的构造函数,然后构造子类自己的this
    super(name);
    this.dogName2 = name;
  }
  // 父类也有一个move方法,我们在子类例自定义move方法,就会重写从Animal继承来的move方法,从而使move方法根据不同的类而实现不同的功能
  move(distanceInMeters: number = 5) {
    console.log('重写了基类的move方法');
    super.move(distanceInMeters);
  }
  bark() {
    console.log('狗叫!');
  }
}
let animal1: Animal = new Animal('赤兔');
let dog1: Dog = new Dog('阿黄');
animal1.move();
dog1.move(10);

这个dog1即使被声明为 Animal类型,也不会调用父类的move方法,因为它的值就是Dog实例

3.静态方法和属性

  1. ES6中提供了 静态方法, ES7中提供了静态属性; TS两者都有
  2. 我们可以认为类具有 实例部分与 静态部分这两个部分。定义静态属性和方法,只需要在对应的属性和方法前面加上static即可
class Animal {
  static PI = 3.14159;
  static isAnimal(param) {
    return param instanceof Animal;
  }
}
let cat = new Animal();
console.log(Animal.PI);
console.log(Animal.isAnimal(cat));
cat.isAnimal(cat);
cat.PI;

通过对象cat上来调用的属性和方法 叫做对象实例的属性和方法
通过类名Animal来调用的 叫静态属性和方法

4.访问修饰符

  1. ts类中修饰符分为3种; public : 公有(所有)默认;
    protected:保护 (父类+子类);private: 私有(本类)
class Animal {
  public name: string;
  //修饰符还可以使用在构造函数参数中,等同于类中定义该属性,使代码更简洁
  //下面的age属性就相当于定义在顶部的 一个实例属性,借助修饰符也可以定义
   public constructor(theName: string, public age: number = 24) {
     this.name = theName;
   }
   public move() {
      console.log(123);
   }
}
let a1 = new Animal('Lucy');
console.log(a1.name, a1.age);

上面的例子中,name 被设置为了 public,所以直接访问实例的 name 属性是允许的
在ts中,所有的类型默认都是public

  1. private: 当成员被标记成 private时,它就不能在声明它的类的外部访问
class Animal {
  // 这个name属性就只能在这个类里面访问,类外部访问就会报错
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
class Dog extends Animal {
  constructor(name) {
   // 派生类的构造函数必须包含super函数的调用
   // 因为父类的构造函数需要一个参数,所以这里我们需要将name参数传递进去
    super(name);
    // console.log(this.name); //属性“name”为私有属性,只能在类“Animal”中访问。所以在派生类里面访问也是不允许的
  }
}
let a1 = new Animal('Lucy');
console.log(a1.name);
  1. protected: 属性和方法 如果是用 protected 修饰,则允许在派生类中访问, private是不允许的
class Animal {
  // 这个name属性就只能在这个类里面访问,类外部访问就会报错
  protected name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
class Dog extends Animal {
  constructor(name) {
    super(name);
    // 这个基类的name属性是 protected受保护的,所以可以在派生类里面访问
    console.log(this.name);
  }
}
let a1 = new Animal('Lucy');
  1. 构造函数被private修饰, 该类不允许被继承或者实例化;只允许被继承
class Animal {
  public name;
  private constructor(name) {
    this.name = name;
  }
  // protected constructor(name) {
  //   this.name = name;
  // }
}
class Cat extends Animal {
  constructor(name) {
    super(name);
  }
}
let a = new Animal('Jack');

5.readonly

  • 只读属性关键字,只允许出现在属性声明或索引签名中
  • 可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化
class Animal {
  readonly name: string;
  // 声明是初始化
  readonly myName: string = '只读属性';
  // 注意如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面
  constructor(name: string, public readonly firstName: string) {
    // 构造函数里面初始化
    this.name = name;
  }
}
let cat2 = new Animal('阿黄', '小白');
console.log(cat2.name, cat2.myName, cat2.firstName);
cat2.name = '张三'; // 编译报错,说不能给一个只读属性分配一个新值

6.抽象类

  • 抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节
  • abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法抽象成员
abstract class Animal {
  name: string = '基类默认值';
  abstract myName: string;
  // 仅仅定义方法的签名,不包含方法体
  abstract makeSound(): void;
  move(): void {
    console.log('动物行走');
  }
}

下面这行代码就会报错, 无法创建抽象类的实例
抽象类不能被实例化, 只能作为基类使用,也就是只能给其他类继承

let aa2 = new Animal()

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 abstract关键字并且可以包含访问修饰符

class Dog extends Animal {
  myName: string = '抽象成员';
  // 编译报错:非抽象类“Dog”不会实现继承自“Animal”类的抽象成员“makeSound”
  // 也就是说我们要将基类的抽象方法在派生类这里再实现一次
  makeSound() {
    console.log(`基类的抽象方法必须在派生类中实现--${this.name}--${this.myName}`);
  }
}
let aa3 = new Dog();
console.log(aa3.makeSound());
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350