基础小咸菜,JS/TS类

这是我参与更文挑战的第 19 天,活动详情查看: 更文挑战

基础小咸菜,JS/TS类

1 类在JS中的表现(ES6)

先上代码:

class Test {
    constructor( params ){
        // 实列成员
        this.name = 'name';
        this.params = params;
        this.testAction = ()=>{
            console.log('构造函数上的方法')
        }
    }
    // 原型方法,定义在原型对象上
    testPublicAction(){
        console.log('我是公共方法')
    }
    // 静态方法 定义在类本身上
    static testStaticAction (){
        console.log('我是静态方法')
    }

}
// es6中没有静态属性,因此添加静态属性可以这么添加
Test.myattr = "类的静态属性";
// 在原型上定义数据成员
Test.prototype.firistname  = "在原型上定义数据成员";

const testone = new Test('我是参数');
console.log(testone.name);
console.log(testone.params);
console.log(testone.firistname); // 打印 在原型上定义数据成员
testone.testPublicAction();
// testone.testStaticAction();// 静态方法放实例中调用报错
Test.testStaticAction();// 正确
testone.testAction();

class TestTwo extends Test{
    constructor(paramsOne,paramsTwo){
        super(paramsOne); // 调用继承Test类的构造函数
        this.testTwoName = paramsTwo;
        this.testTwoAction = ()=>{
            console.log('我是类testTwo上的方法')
        }
    }

    // 可以在静态方法中通过super调用继承的类上定义的静态方法
    static handExtendsParentAction (){
        super.testStaticAction();
    }
}

const testtow = new TestTwo('我是一个新参数一','我是一个新参数二');
console.log(testtow)
testtow.testPublicAction();
TestTwo.handExtendsParentAction();
复制代码

1.1 在es6类中的构造函数

constructor会告诉解释器在使用new操作符创建新的实例时调用这个2.函数。

1.2 实例化

使用new调用类的构造函数会执行如下操作

  1. 在内存中创建一个新的对象
  2. 在这个新对象内部的__proto__指针被赋值为构造函数的prototype属性
  3. this指向新对象
  4. 指向构造函数的代码
  5. 如果构造函数返回非控对象这返回该对象;如果为空则返回刚创建的对象

补充:其实类也是一种特殊的函数

2. 继承

es6实现继承比使用原型链实现继承简单多了,直接使用extends关键字即可

2.1 super()

派生类使用super关键字引用他们的原型。

  • 在静态方法中可以通过super()调用继承类上的定义的静态方法
  • super()只能在派生类构造函数和静态方法中使用
  • 不能单独引用super()关键字,要么用他调用构造函数,要么用它引用静态方法
  • 调用super()会调用父类构造函数,并将返回的实例赋值给this
  • super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入
  • 如果没有定义类构造函数,在实例化派生类会调用super而且会传入所有传给派生类的参数
  • 在类的构造函数中不能再调用super()之前调用this
  • 如果在派生类中显示的定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象

2.2 抽象基类

有时候需要这样一个类:提供给其他类继承,但本身不会被实例化
实现如下:

class BaseClass {
    constructor(){
        if(new.target === BaseClass){
            throw new Error('这是一个抽象基类')
        }
    }
}
复制代码

3 ts的类

其实ts的类和es6都差不多,多了如下:

3.1 public

public表示公共的,用来指定在创建实例后可以通过实例访问的,也就是类定义的外部可以访问的属性和方法。默认是 public,但是 TSLint 可能会要求你必须用修饰符来表明这个属性或方法是什么类型的。

class Point {
  public x: number;
  public y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  public getPosition() {
    return `(${this.x}, ${this.y})`;
  }
}
复制代码

3.2 private

private修饰符表示私有的,它修饰的属性在类的定义外面是没法访问的:


class Parent {
  private age: number;
  constructor(age: number) {
    this.age = age;
  }
}
const p = new Parent(18);
console.log(p); // { age: 18 }
console.log(p.age); // error 属性“age”为私有属性,只能在类“Parent”中访问
console.log(Parent.age); // error 类型“typeof ParentA”上不存在属性“age”
class Child extends Parent {
  constructor(age: number) {
    super(age);
    console.log(super.age); //error 通过 "super" 关键字只能访问基类的公共方法和受保护方法
  }
}
复制代码

3.3 protected

protected修饰符是受保护修饰符,和private有些相似,但有一点不同,protected修饰的成员在继承该类的子类中可以访问,上面那个例子,把父类 Parent 的 age 属性的修饰符 private 替换为 protected:

class Parent {
  protected age: number;
  constructor(age: number) {
    this.age = age;
  }
  protected getAge() {
    return this.age;
  }
}
const p = new Parent(18);
console.log(p.age); // error 属性“age”为私有属性,只能在类“ParentA”中访问
console.log(Parent.age); // error 类型“typeof ParentA”上不存在属性“age”
class Child extends Parent {
  constructor(age: number) {
    super(age);
    console.log(super.age); // undefined
    console.log(super.getAge());
  }
}
new Child(18)
复制代码

protected还能用来修饰 constructor 构造函数,加了protected修饰符之后,这个类就不能再用来创建实例,只能被子类继承,这个需求 ES6 的类的时候需要用new.target来自行判断,而 TS 则只需用 protected 修饰符即可

3.4 readonly 修饰符

在类里可以使用readonly关键字将属性设置为只读

class UserInfo {
  readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}
const user = new UserInfo("Lison");
user.name = "haha"; // error Cannot assign to 'name' because it is a read-only property
复制代码

设置为只读的属性,实例只能读取这个属性值,但不能修改。

3.5 存取器

这个也就 ES6 标准中的存值函数和取值函数,也就是在设置属性值的时候调用的函数,和在访问属性值的时候调用的函数,用法和写法和 ES6 的没有区别:

class UserInfo {
  private _fullName: string;
  constructor() {}
  get fullName() {
    return this._fullName;
  }
  set fullName(value) {
    console.log(`setter: ${value}`);
    this._fullName = value;
  }
}
const user = new UserInfo();
user.fullName = "Lison Li"; // "setter: Lison Li"
console.log(user.fullName); // "Lison Li"
复制代码

3.6 抽象类

抽象类一般用来被其他类继承,而不直接用它创建实例。抽象类和类内部定义抽象方法,使用abstract关键字

3.7 类类型接口

使用接口可以强制一个类的定义必须包含某些内容,先来看个例子:

interface FoodInterface {
  type: string;
}
class FoodClass implements FoodInterface {
  // error Property 'type' is missing in type 'FoodClass' but required in type 'FoodInterface'
  static type: string;
  constructor() {}
}
复制代码

上面接口 FoodInterface 要求使用该接口的值必须有一个 type 属性,定义的类FoodClass要使用接口,需要使用关键字implementsimplements关键字用来指定一个类要继承的接口,如果是接口和接口、类和类直接的继承,使用extends,如果是类继承接口,则用implements

有一点需要注意,接口检测的是使用该接口定义的类创建的实例,所以上面例子中虽然定义了静态属性 type,但静态属性不会添加到实例上,所以还是报错,所以我们可以这样改:

interface FoodInterface {
  type: string;
}
class FoodClass implements FoodInterface {
  constructor(public type: string) {}
}
复制代码

当然这个需求抽象类实现:


abstract class FoodAbstractClass {
  abstract type: string;
}
class Food extends FoodAbstractClass {
  constructor(public type: string) {
    super();
  }
}
复制代码

3.8 接口继承类

接口可以继承一个类,当接口继承了该类后,会继承类的成员,但是不包括其实现,也就是只继承成员以及成员类型。接口还会继承类的privateprotected修饰的成员,当接口继承的这个类中包含这两个修饰符修饰的成员时,这个接口只可被这个类或他的子类实现。


class A {
  protected name: string;
}
interface I extends A {}
class B implements I {} // error Property 'name' is missing in type 'B' but required in type 'I'
class C implements I {
  // error 属性“name”受保护,但类型“C”并不是从“A”派生的类
  name: string;
}
class D extends A implements I {
  getName() {
    return this.name;
  }
}
复制代码

3.9 在泛型中使用类类型

这里我们先来看个例子:

const create = <T>(c: { new (): T }): T => {
  return new c();
};
class Info {
  age: number;
}
create(Info).age;
create(Info).name; // error 类型“Info”上不存在属性“name”
复制代码

在这个例子里,我们创建了一个一个 create 函数,传入的参数是一个类,返回的是一个类创建的实例,这里有几个点要讲:

参数c的类型定义中,new()代表调用类的构造函数,他的类型也就是类创建实例后的实例的类型。
return new c()这里使用传进来的类 c 创建一个实例并返回,返回的实例类型也就是函数的返回值类型。
所以通过这个定义,TS 就知道,调用 create 函数,传入的和返回的值都应该是同一个类类型。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享