IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 《TypeScript》入门与精通 - 一文了解TS中的类 -> 正文阅读

[JavaScript知识库]《TypeScript》入门与精通 - 一文了解TS中的类

前言

在JavaScript中,其实是没有其他语言中类的概念的,即使是ES6的class,不过也是Propotype的语法糖,而在TypeScript中,它对类这个概念,进行了一定程度的加强,来更好的约束我们的Javascript,用法基本上和ES6的class大同小异;
?

基础用法

先来定一个最简单的类

class Human {
	// ...
}

这就是简单的一个类型,如果要在类中加上属性和方法,如下

class Human {
    name = "oliver";
    getName() {
        return this.name;
    }
}

同样,在Ts中的类和ES6语法中的类一样,可以继承

class Human {
    name = "oliver";
    getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    age = 18;
    getAge() {
        return this.age;
    }
}

const user = new User();
console.log(user.getName())	// oliver

打印出来的结果是来自于上级的oliver,另外这个上级我们称作为父类,那么这样便实现了继承,另外如果子类的属性或方法和父类重叠了,那么子类的属性或方法会覆盖父类的属性或方法,比如:

class Human {
    name = "oliver";
    getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    age = 18;
    getAge() {
        return this.age;
    }
  	getName() {
        return "test";
    }
}

const user = new User();
console.log(user.getName())	// test

那么如果要在子类的方法中使用父类的方法,有没有办法,比如,在子类的getName中返回父类的getName的值,那么就需要使用到另外一个关键字,super,在子类方法中使用super就相当于在使用父类:

class Human {
    name = "oliver";
    getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    age = 18;
    getAge() {
        return this.age;
    }
    getName() {
        return super.getName();
    }
}

const user = new User();
console.log(user.getName()); // oliver

?

访问类型

在Ts中,类的常见的访问类型一般就是三种:私有类型-private,保护类型-protected,公共类型-public;

私有类型(private)

简单的说,就是一旦声明这个属性或方法为私有类型,那么就代表这个属性或方法仅仅能在这个类的内部使用,子类使用extends是继承不到这个私有类型的,示例如下:

class Human {
    private name = "oliver";
    public getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    public age = 18;
    public getAge() {
        return this.age;
    }
    public getName() {
        return super.name; // 报错,private属性是无法被继承到的
    }
    public getNameFunc() {
        return super.getName(); // 正确
    }
}

const user = new User();
console.log(user.name); // 报错,因为name是private,无法访问
console.log(user.getName()); // test

通过示例可以知道,一旦属性被定义成了private,那么这个属性无法被子类继承到,也无法被实例化的对象使用到,这个属性仅仅能被定义private属性的类使用;

公共类型(public)

这个类型通过名字就可以知道,是公共的,可以任意地方调用的,包括类内和类外,实际上,所有我们没有定义类型的属性或方法,它在内部都会给其一个默认的类型:public,比如上例的这段代码:

class Human {
    name = "oliver";
    getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    age = 18;
    getAge() {
        return this.age;
    }
    getName() {
        return super.getName();
    }
}

const user = new User();
console.log(user.getName()); // oliver

这段代码中的两个类都没有使用到public,但是实际上,等同于下面这段代码:

class Human {
    public name = "oliver";
    public getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    public age = 18;
    public getAge() {
        return this.age;
    }
    public getName() {
        return super.getName();
    }
}

const user = new User();
// 因为是public,所以实例对象也可以使用
console.log(user.getName()); // oliver

也正是因为使用了public这个关键词,所以,我们在实例化后,仍然可以访问类上面的属性和方法;
?

保护类型(protected)

保护类型则是介于public和private之间,它代表,它可以被子类继承,但是无法被实例化对象继承,比如:

class Human {
    protected name = "oliver";
    public getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    public age = 18;
    public getAge() {
        return this.age;
    }
    public getName() {
        return super.name; // 正确
    }
    public getNameFunc() {
        return this.name(); // 正确
    }
}

const user = new User();
console.log(user.name); // 报错,因为name是protected,无法访问
console.log(user.getName()); // test

实例化对象中无法调用类上定义的protected的属性或方法,但是子类上是能继承的,子类中的使用和public属性没有区别,这个就是protected;
?

小结

总的来说:、

  • public类型:本类中可以使用,子类中可以使用,实例对象可以使用;
  • private类型:本类中可以使用,子类中不能使用,实例对象不可以使用;
  • protected类型:本类中可以使用,子类中可以使用,实例对象不可以使用;

如果还不清晰,那么看一个例子:

class Human {
    protected name = "oliver";
    private sex = "男";
    public getName() {
        return this.name;
    }
}

// 使用关键字extends
class User extends Human {
    public age = 18;
    public getAge() {
        return this.age; // 正确
    }
    public getName() {
        return super.name; // 正确,因为是protected,可以被子类调用
    }
    public getSex() {
        return super.sex; // 报错,因为是private,不能被子类调用
    }
    protected getNameFunc() {
        return super.getName(); // 正确,因为是public
    }
}

const user = new User();
console.log(user.name); // 报错,因为name是protected,能被子类调用,且无法被实例化对象调用
console.log(user.getName()); // 正确,因为是public
console.log(user.sex; // 报错,因为sex是private,无法被子类调用,也无法被实例化对象调用

构造器

构造器,也就是contructor,它是一种用于创建和初始化class创建的对象的特殊方法,先看个例子:

// 基本写法
class Human {
    public name: string;
		// 构造器
    constructor(name: string) {
        this.name = name;
    }
}

const user = new Human("oliver");
console.log(user.name); // oliver

这里定义了一个类,class,它有一个私有类型的属性name,name的类型是string,并且在构造器中对其进行了初始化,初始化的值来自于new时候传入的参数,另外在TS中是可以简写的,下面这段代码和上例效果完全相同

// 简化写法
class Human {
  	// 构造器
    constructor(public name: string) {}
}

const user = new Human("oliver");
console.log(user.name); // oliver

那么,子类能继承到这个属性吗,答案是可以的,先再看下面这一个例子:

class Human {
    constructor(public name: string) {}
}

class User extends Human {
    getName() {
        return this.name;
    }
}

const user = new User("oliver");
console.log(user.getName()); // oliver

因为是public属性,那么子类是可以继承到name属性的,这么写没问题,那么子类如果也有构造器怎么办?那么再看一个例子:

class Human {
    constructor(public name: string) {}
}

class User extends Human {
    constructor(public age: number, name: string) {
        super(name);
    }
    getName() {
        return this.name;
    }
    getAge() {
        return this.age;
    }
}

const user = new User(20, "oliver");
console.log(user.getName()); // oliver
console.log(user.getAge()); // 20

这个例子中存在子类和父类,两个类都有构造器,特别的是,在子类的构造器constructor中使用了一个super函数,这就有点奇怪了,这个用法和上文中的不一样,上文的用法是super.getName()这种,这里为啥是一个函数,实际上,super在方法中使用,代表的是父类,用法就是类似于super.getName(),而在构造函数中,它就可以用作一个函数,代表的是父类的contructor函数,这样我们就可以将一些初始化的值赋予父类使用了,否则父类没办法接受实例化对象的参数;
?

到这里,又有一个新问题,如果父类的constructor中没有参数,那么怎么使用,实际上这种只要使用一下super就好了,比如:

class Human {}

class User extends Human {
    constructor(public age: number, public name: string) {
        super();
    }
    getName() {
        return this.name;
    }
    getAge() {
        return this.age;
    }
}

const user = new User(20, "oliver");
console.log(user.getName()); // oliver
console.log(user.getAge()); // 20

Getter和Setter

getter和setter,主要用途是对私有属性进行读取和设置,可能有同学会问,这个有什么用,既然都设置成私有属性了,那么就是希望不对它进行修改,怎么说呢,实际上是不对的,私有属性的作用却是是为了不将属性直接对外暴露,但是不代表某些情况下不能对他进行修改,因此Getter和Setter还是很有必要的,直接看个例子吧:

class User {
    constructor(private _age: number, private _name: string) {}
    get name() {
        return this._name;
    }
    get age() {
        return this._age;
    }
}

const user = new User(20, "oliver");
console.log(user.name); // oliver
console.log(user.age); // 20

这个就是Getter的用法,例子中定义了两个私有属性_age和_name,另外定义了两个getter,分别获取这两个私有属性,就这么简单,同理Setting也是这么简单;

class User {
    constructor(private _age: number, private _name: string) {}
    get name() {
        return this._name;
    }
    get age() {
        return this._age;
    }
    set name(value: string) {
        this._name = value;
    }
    set age(value: number) {
        this._age = value;
    }
}

const user = new User(20, "oliver");
user.age = 18;
user.name = "demo";
console.log(user.name); // demo
console.log(user.age); // 18

值得注意的是,Getter和Setter看上去好像是一个方法,但实际上是属性,因此在调用的时候不需要加括号,以及,set中设置return是不会生效的;

静态属性和静态方法

static,这又是一个新的关键词,在MDN上的解释:类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。
什么意思呢,简单的说,就是这个方法不能被实例调用,只能在类上调用,讲到这里,有些同学可能会问这个和protected感觉好像,但实际上,这两个可以说完全不是一个东西,举个例子

class User {
    protected static name: string;
}

可以看出,私有类型和静态属性是可以并存的,这两者不是一个东西,值得注意的是,所有的访问类型,都是实例化后才生效的,也就是说,如果不实例化而是直接调用,public,private这些没有意义;
?

接着再说上面这段代码,真要去使用的时候会发现报错,提示:静态属性“name”与构造函数“User”的内置属性函数“name”冲突,好家伙,恭喜你,找到一个保留字,不知道为啥,我个人查了许久没有找到答案,网上流传的解释是:static下的name是一个保留词,不能使用(如果有人找到了合理的答案,记得告诉博主);
?

接着说static,我们还是直接看一个示例吧,题目:有一个构造函数,它只能被new一次,也就是设计模式中的单例模式,很多库文件就是基于单例模式实现的,比如VueX,VueX使用的就是单例模式,只有第一次使用的使用才会注入一个store,如果被多次Vue.use(vuex),那么它不会被多次覆盖生效,达到的效果如下

解答:

class User {
    private static User: User;
    private constructor() {}

    static init() {
        if (!this.User) {
            this.User = new User();
        }
        return this.User;
    }
}

const user1 = User.init();
const user2 = User.init();
console.log(user1 === user2); // true

只有第一次调用init的时候才会去进行new,第二次开始,都是将其已经new好的实例返回回去,这样就达到了单例模式;
?

只读属性

好吧,有一个新关键字,readonly,根据字面意思,代表着只读属性,用法也比较简单,直接看例子:

class User {
    private readonly name: string;
    constructor(name: string) {
        this.name = name;
    }
}

const user = new User("oliver");
user.name = "demo";	// 报错

可以看出,尝试修改的时候会直接报错,只读属性是不允许被修改的;
?

抽象类

关键字:abstract,使用abstract定义的类就是抽象类,抽象类和普通类最大的区别就是抽象类不能被实例化,也就是不能被new,比如:

abstract class Human {}
new Human(); // 报错,无法被实例化

那么抽象类的作用是什么,主要的作用是抽离贡性的东西,比如:

// 抽象类
abstract class Human {
    // 抽象类中的抽象方法
    abstract getSex(): string;
}

class Man extends Human {
    getSex() {
        return "男";
    }
}

class Woman extends Human {
    getSex() {
        return "女";
    }
}

我们定义了一个抽象类Human,他里面有一个抽象方法,规定了所有继承这个抽象类的类都必须包含getSex这个方法,并且这个方法返回的类型是字符串,讲到这里,有的同学可能会问,这个和接口Interface好像, 用法几乎一样,没错,是挺像的,区别在于,抽象类是对类进行共性的剥离,并且抽象类的内部也是可以存在具体实现的功能的代码的,比如:

// 抽象类
abstract class Human {
  	getPlace(){
    	return "江苏"
    }
    // 抽象类中的抽象方法
    abstract getSex(): string;
}

class Man extends Human {
    getSex() {
        return "男";
    }
}

class Woman extends Human {
    getSex() {
        return "女";
    }
}

这里的getPlace就是一个具体的实现代码,继承Human的Man和Woman都会存在这个方法,而interface,它仅仅是一个规范,它内部存在的都是对代码的约束规则,它内部实际上是不存在实际代码的;这是最大的区别,但如果仅仅使用抽象类作为一种规范,它内部不存在具体的实现代码,那它和interface实际上没什么区别,都是一种对代码的约束规范;
?

小结

本文主要讲述了:类的定义,它的基本用法,然后讲了内部存在的三种访问类型,以及当new的时候回使用到constructor这个构造器,接着又描述了Getter和Setter,静态属性,只读属性,最后描述了抽象类这个和接口很近似的存在;

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-03 12:57:17  更:2021-12-03 12:57:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:43:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码