类的声明
写法和es6一样
class Person{
// 这里要声明有那些属性
name:string // 前面省略了public
age: number = 20 // 默认参数
constructor(n:string){ // 实例化后会触发的方法,n只是个形参,不一定要和name重名
this.name = n
}
run():void{
console.log('run')
}
}
let p = new Person('a')
let p1: Person = new Person('b')
特别说明:初学者可能会有这样的疑问,为啥在类中开头定义了参数,constructor还要再定义一次呢?
class User {
// 其实这样定义的是属性,其中如果不用constructor来获得属性值的来源,那么一定要手动去赋值其他或者给个默认参数
username: string | undefined
password: string | undefined
}
let u = new User()
u.username = 'xiaoming'
u.password = '1234'
等同于
class User {
username: string
password: string
constructor(username: string, password: string) { // constructor就是用参数给属性赋值
this.username = username
this.password = password
}
}
let u = new User('xiaoming', '1234')
存取器
在类中怎么实现get和set(存取器)
class Son {
firstName: string;
lastName: string = 'Stark';
constructor(firstName: string) {
this.firstName = firstName;
}
}
class GrandSon extends Son {
constructor(firstName: string) {
super(firstName);
}
get myLastName() {
return this.lastName;
}
set myLastName(name: string) {
if (this.firstName === 'Tony') {
this.lastName = name;
} else {
console.error('Unable to change myLastName');
}
}
}
const grandSon = new GrandSon('Tony');
console.log(grandSon.myLastName); // => 触发get函数,返回带有默认值的"Stark"
grandSon.myLastName = 'Rogers';
console.log(grandSon.myLastName); // => 触发set函数,此时firstName ='Tony',所以可以走if里的逻辑,"Rogers"
const grandSon1 = new GrandSon('Tony1');
grandSon1.myLastName = 'Rogers'; // => 触发set函数,此时firstName !='Tony'所以"Unable to change myLastName"
继承
和es6一样的,用关键词extends
和super
。
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
class Son extends Person {
constructor(name: string) { // ts规定,当使用了constructor必须调用super
super(name) // 调用父类的 constructor(name)
}
run() {
return super.work() // 可以直接调用父类的work方法
}
}
let s = new Son("son")
注意:如果继承的方法与子类新增的同名,那么子类在实例化调用的时候会先去找子类身上的,没有再去找父类定义的。
tips:派生类通常被称作子类,基类也被称作超类(或者父类)。
修饰符
public
:表示公有,在父类定义函数中和外,以及子类都能直接以任何形式访问。protected
:表示保护,在父类定义函数中和子类可以访问,但类定义函数外不能以获取属性形式访问。private
:表示私有,只有在父类定义函数中访问,子类函数内部无法访问(但可通过外面调用继承的父类方法去访问)。
不加修饰符默认为public。
class Person{
protected name:string
private age:number
constructor(n:string, a:number){ // 实例化后会触发的方法
this.name = n
this.age = a
}
run():void{
console.log(this.name + 'run')
}
tell():void{
console.log('我'+this.age+'岁') // 私有只能在当前类中使用
}
}
class Son extends Person{
constructor(name: string, age:number) {
super(name, age)
}
//tell():void{
// console.log(this.age) // 如果这里这样写就会报错不能使用私有类型
//}
}
let p = new Person('p', 18)
// console.log(p.name) 外部访问保护类型报错
// console.log(p.age) 外部访问私有类型报错
p.run()
p.tell() // 从内部访问私有类型可以访问
let s = new Son('a', 6)
s.run() // 可以运行,保护类型子类可以访问
s.tell() // 从内部访问私有类型可以访问
补充:如果父类连constructor
都设置private
和protected
,那子类就不允许继承里面的东西。
tips:个人觉得可以这样子记。爸爸标记了public的东西A,意思是儿子在家里(class函数内)和家外面(class函数外),可以随便拿爸爸的东西A;爸爸标记了protected的东西B,意思是儿子在家里可以随便拿爸爸的东西B,但是在外面需要经过爸爸的同意(通过调用继承的父级方法)才能拿东西B;爸爸标记了private的东西C,意思是爸爸的私人物品,儿子只能在外面求爸爸要(通过调用继承的父级方法)才给。
静态属性与静态方法
静态属性:存在于类这个特殊的对象上,而不是类的实例上,所以我们可以直接通过类访问的属性。
静态方法:就是不在构造函数中赋予的方法(实例方法),而是实例化对象后,给对象后添加的方法。
怎么看第三方库哪些是静态方法,哪些是实例方法。例如JQ:
function $(el){
return new Base(el)
}
function Base(el){
this.el = 获取dom节点
this.css = function(attr, value){ // 实例方法
this.el.style.attr = value
}
}
$.get = function(){ // 静态方法
// get请求逻辑
}
// 所以会看到这么使用
$("#box").css('color', 'red')
$.get('url', function(){})
在TS中静态方法和静态属性是写在构造函数中的,用关键词static
:
class Person{
public name:string // 前面省略了public
static age = 12
constructor(n:string){ // 实例化后会触发的方法
this.name = n
}
run():void{ // 实例方法
console.log('run')
}
static work() { // 静态方法
console.log('不能划水')
}
static tell() {
console.log(this.age)
}
}
Person.work() // 可以不用实例化直接使用(在没有使用类中的非静态属性情况下)
Person.tell() // 调用了静态属性
let p = new Person('name')
p.work() // ;实例化后使用静态方法或属性直接报错
再来个简单例子,ts中的Math是一个内置的构造函数,你可以直接使用他的方法,例如Math.pow(),是因为他在构造函数内部声明的是静态方法。
引用:基于静态属性的特性,我们往往会把与类相关的常量、不依赖实例 this 上下文的属性和方法定义为静态属性,从而避免数据冗余,进而提升运行性能。
只读
用readonly修饰只读属性:
class Person{
readonly name;
// private readonly xxx 有其他修饰符要写在后面
public constructor(name) {
this.name = name;
}
}
let p = new Person('a')
p.name = 'b' // 修改就报错
多态
可以理解为,父类定义一个方法不去实现,让继承的子类去实现,而且每个子类有不同的表现。
例子:
class Person {
public: string;
constructor(name: string) {
this.name = name
}
run() {
console.log(‘什么是多态’)
}
}
class Son extends Person{
constructor(name: string) {
super(name)
}
run() {
console.log(‘重写这个方法的具体内容’)
}
}
有点方法重写的意思在里面。
抽象方法
先说什么是抽象类:提供其他类继承的基类,啥意思呢,主要用来定义继承标准,要求子类也必须服从这个标准。
用abstract
关键词定义抽象方法。
abstract class Person{
name: string
constructor(name: string){
this.name = name
}
abstract run():any; // 抽象方法只能放在抽象类中,如果类不声明abstract就会报错。抽象类中的抽象方法不包含具体实现。
}
// let p = new Person() 抽象类不能被实例化,会报错
class Son extends Person{
constructor(name: string){
super(name)
}
run(){ // 抽象类方法必须!!在子类继承并且实现,否则报错
xxx
}
}
和多态很像,感觉抽象就是多态的强制版233 。
类的类型
类也可以作为一个变量的类型:
class A {
name: string;
constructor(name: string) {
this.name = name;
}
}
const a1: A = {}; // 报错
const a2: A = { name: 'a2' };
个人猜测vue3+ts里很多自带的变量类型很有可能就是自封的类。