TypeScript学习日记-类

188 阅读6分钟

ES5中本无类这一概念, 多是利用函数所具有的局部作用域进行类的封装

而ES6与TS中提出了类这一概念

ES5的类

声明

属性和方法可以直接在构造函数或者原型链中声明, 这样声明的方法为实例方法

二者区别是原型链声明的方法与属性是多个实例所共享的, 而构造函数生成的方法是各实例独占的,互不影响的

function Person() {
    this.name = '张三';
    this.age = '20';
    this.run = function() { console.log(`${this.name}runing`); }; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() { console.log(`${this.name}working`); };// 实例方法(原型链)

const p1 = new Person();
const p2 = new Person();

console.log(p1.run === p2.run); // false
console.log(p1.work === p2.work); // true
继承

ES5通过 原型链或者对象冒充 的方式实现继承

function Person() {
  this.name = '张三';
  this.age = '20';
  this.run = function() {
    console.log(`${this.name}runing`);
  }; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() {
  console.log(`${this.name}working`);
}; // 实例方法(原型链)

// 对象冒充实现继承
// 对象冒充只能继承构造函数中属性与方法,但是原型链无法继承
function Man() {
  Person.call(this);
}
const man = new Man();
console.log(man); // Man {name: "张三", age: "20", run: ƒ} 但是prototype种没有对应的work函数
man.run(); // 张三runing
// man.work(); // 报错

// 原型链实现继承
// 原型链实现继承,对构造函数和原型链方法都可以进行继承
function Worman() {}
Worman.prototype = new Person();
const worman = new Worman();
console.log(worman); // { __proto__: { name: "张三", age: "20", run: ƒ, __proto__: {sex: "man", work: f }}}
worman.run(); // 张三runing
worman.work(); // 张三working (如果函数改用箭头函数, 则this指向widow.从而返回是working))

看似原型链继承就足够用了,但是在构造函数的属性需要赋值时, 原型链就会有问题出现,

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.run = function() {
    console.log(`${this.name}runing`);
  }; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() {
  console.log(`${this.name}working`);
}; // 实例方法(原型链)

// 对象冒充实现继承
// 对象冒充可以给子类赋值
function Man(name, age) {
  Person.call(this, name, age);
}
const man = new Man('李华', 18);
console.log(man); // Man {name: "李华", age: 18, run: ƒ}
man.run(); // 李华runing
// man.work(); // 报错

// 原型链实现继承
// 原型链可以获取原型链上方法和属性
function Worman(name, age) {}
Worman.prototype = new Person();
const worman = new Worman('王梅', 20); // 子类的实例化无法给父类传参
console.log(worman); // { __proto__: { name: undefined, age: undefined, run: ƒ, __proto__: {sex: "man", work: f }}}
worman.run(); // undefinedruning
worman.work(); // undefinedworking

由此得出, 对象冒充可以给子类赋值同时原型链可以获取原型链上方法和属性, 此时,便可以使用原型链 + 对象冒充一起实现继承

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.run = function() {
    console.log(`${this.name}runing`);
  }; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() {
  console.log(`${this.name}working`);
}; // 实例方法(原型链)

// 原型链实现继承
function Worman(name, age) { Person.call(this, name, age); }
Worman.prototype = new Person(); // Worman.prototype = Person.prototype 也行
const worman = new Worman('王梅', 20); // 子类的实例化无法给父类传参
console.log(worman); // { __proto__: { name: '王梅', age: 20, run: ƒ, __proto__: {sex: "man", work: f }}}
worman.run(); // undefinedruning
worman.work(); // undefinedworking

静态

也可以直接对类进行方法添加, 这种方法是静态方法

function Person() {
  this.zname = '张三'; // 实例属性
  this.run = function() {
    console.log(`${this.zname} + ${this.wname}runing`);
  }; // 实例方法
}

Person.wname = '王五'; // 静态属性
Person.work = function() {
  console.log(`${this.zname} + ${this.wname} working`);
}; // 静态方法

const p = new Person();

p.run(); // 张三 + undefinedruning
// p.work(); // 报错
// Person.run(); // 报错
Person.work(); // undefined + 王五 working

TS中的类

声明
class Person {
  name: string; // 属性
  constructor(n: string) {
    // 构造函数,实例化触发方法
    this.name = n;
  }
  getName(): string {
    console.log(this.name);
    return this.name;
  }
  setName(name: string): void {
    this.name = name;
  }
}
var p = new Person('张三');
p.getName(); // 张三
继承

TS实现继承主要使用extends与super实现

class Person {
  name: string;
  constructor(n: string) {
    this.name = n;
  }
  getName(): string {
    console.log(this.name);
    return this.name;
  }
  setName(name: string): void {
    this.name = name;
  }
}
// 使用extends继承
class Man extends Person {
  constructor(name: string) {
    super(name); // super,初始化父类构造函数
  }
  work(): void {
    console.log(this.name + 'working');
  }
}
const man = new Man('李四')
man.getName() // 李四
man.work() // 李四working

但是父子类之间若方法重名,则优先执行子类方法

class Person {
  name: string;
  constructor(n: string) {
    this.name = n;
  }
  getName(): string {
    console.log(this.name);
    return this.name;
  }
  setName(name: string): void {
    this.name = name;
  }
}
// 使用extends继承
class Man extends Person {
  constructor(name: string) {
    super(name); // super,初始化父类构造函数
  }
  getName(): string {
    console.log(this.name + '在子类');
    return this.name;
  }
}
const man = new Man('李四');
man.getName(); // 李四在子类 (但是函数的形式还是需要按照父类的同名函数的形式构建)
静态

静态属性只能静态方法使用,同时静态方法只有类可使用

class Person {
  public name: string;
  static age: number = 20; // 静态属性
  constructor(name) {
    this.name = name;
  }
  // 实例方法
  run() {
    console.log(this.name + 'run');
	// console.log(this.age + 'run'); // 报错,因为静态属性只能在静态方法中使用
  }
  // 静态方法
  static work() {
    // console.log(this.name+'work') // 报错,因为静态方法只能用静态属性
    console.log('work' + this.age);
  }
}

var p = new Person('李华');
// 实例方法调用
p.run();
// Person.run(); // 报错
// 静态方法调用
Person.work();
// p.work(); // 报错

权限
public

公共,在类里面,子类,类外面都可以访问,同时也是默认值

class Person {
    public name: string
    constructor(n: string) {
        this.name = n
    }
    getName(): void {
        console.log(this.name)
    }
}

class Man extends Person {
    constructor(name: string) {
        super(name);
    }
    run(): void {
        console.log(this.name)
    }
}

var p = new Person('张三')
var m = new Man('李四')

// 内部访问
p.getName() // 张三
// 外部访问
console.log(p.name) // 张三
// 子类访问
m.run() // 李四
protected

保护, 在类与子类可以访问,但类外部无法访问

class Person {
    protected name: string
    constructor(n: string) {
        this.name = n
    }
    alertName(): void {
        console.log(this.name)
    }
}

class Man extends Person {
    constructor(name: string) {
        super(name);
    }
    run(): void {
        console.log(this.name)
    }
}

var p = new Person('张三')
var m = new Man('李四')

// 内部访问
p.alertName() // 张三
// 外部访问
// console.log(p.name) // 属性“name”受保护,只能在类“Person”及其子类中访问
// 子类访问
m.run() // 李四
private

私有,只有类可以访问,子类和外部不可访问

class Person {
    private name: string
    constructor(n: string) {
        this.name = n
    }
    alertName(): void {
        console.log(this.name)
    }
}

class Man extends Person {
    constructor(name: string) {
        super(name);
    }
    run(): void {
        console.log(this.name)
    }
}
var p = new Person('张三')
var m = new Man('李四')

// 内部访问
p.alertName() // 张三
// 外部访问
// p.name // 属性“name”为私有属性,只能在类“Person”中访问。
// 子类访问
// m.run() //  属性“name”为私有属性,只能在类“Person”中访问。
只读

使用readyonly添加只读属性

class Person {
    readonly name: string
    constructor(n: string) {
        this.name = n
    }
    alertName(): void {
        console.log(this.name)
    }
}

var p = new Person('张三')

// p.name = '李四' // 无法分配到 "name" ,因为它是只读属性。
多态

父类方法父类不实现,尤其子类实现,且子类实现形式各不同

也是继承的一种表现

class Animal {
    name: string
    constructor(name: string) {
        this.name = name
    }
    // 定义方法由子类实现
    eat() {
        console.log('吃的方法')
    }
}

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 + '吃鱼'
    }
}
抽象

使用abstract关键子定义抽象类与方法

抽象类提供其他类继承的基类, 并不能直接被实例化.用于定义标准

抽象类的抽象方法必须在派生类中实现

不同于接口,抽象类可以包含成员的实现细节

抽象方法只能在抽象类中

抽象亦是中多态

abstract class Animal {
    public name: string;
    constructor(name: string) {
        this.name = name
    }
    abstract eat(): any;
}

// var a=new Animal() // 无法创建抽象类的实例
class Dog extends Animal {
	// >>>>> 这里忘记写name, 标记下
    constructor(name) {
        super(name)
    }
    // eat 不声明会报错
    eat() {
        return this.name + '吃骨头'
    }
}

今天(9月17号)看项目时发现抽象类的一种用法,在此更新下心得

abstract class Animal {
    public name: string;
    constructor(name: string) {
        this.name = name
    }
    abstract eat(): any;
    see(){
        console.log('see');       
    }
}

// var a=new Animal() // 无法创建抽象类的实例
class Dog extends Animal {
    constructor(name) {
        super(name)
    }
    // eat 不声明会报错
    eat() {
        return this.name + '吃骨头'
    }
    see(){
        super.see() // 此时则会执行Animal中see的方法
        console.log(this.name + 'see')
    }
}

const dog = new Dog('dog')
dog.see() // see dogsee

这样可以把抽象类当作一种类别最基础的实现, 继承该抽象类的类可以再某些方法使用super执行抽象类中的方法, 并可以继续执行在继承类中相同方法的剩余代码

abstract class Animal {
    public name: string;
    constructor(name: string) {
        this.name = name
    }
    abstract eat(): any;
    see(){
        console.log('see');       
    }
}

// var a=new Animal() // 无法创建抽象类的实例
class Dog extends Animal {
    constructor(name) {
        super(name)
    }
    // eat 不声明会报错
    eat() {
        return this.name + '吃骨头'
    }
    see(){
        console.log(this.name + 'see')
    }
}

const dog = new Dog('dog')
dog.see() // dogsee

但是super.see(), 则继承类中的see等同于重写了抽象类中的see