//使用class关键字定义一个类
class Person {
//定义实例属性
//ps:static name 这样会报错,因为类中有一个默认的name属性,所以不能用static修饰name,可以换个别的名字
name: string = '孙悟空';
//readonly表示只读属性,无法修改
//readonly name:string = '孙悟空';
//定义静态属性
static age: number = 18;
//定义方法,如果以 static 修改也变成了静态方法
sayHello(){
console.log("Hello");
}
}
console.log(Person.age);
const person = new Person();
console.log(person.name);
person.sayHello();
TS 中的对象是结构化的,在使用对象前,就可以根据需求,提前设计好对象的结构(建立一种契约,约束对象结构)
let person: {
name: string
age: number
sayHello: () => void
saySomething: (sth: string) => void
sum: (num1: number, num2: number) => number
}
person = {
name: '孙悟空',
age: 18,
sayHello() {
console.log("Hello");
},
saySomething(sth: string) {
console.log(sth)
},
sum(num1: number, num2: number) {
return num1 + num2
}
}
class Dog{
name:string;
age:number;
//构造函数会在对象创建时执行
constructor(name:string,age:number) {
//this表示当前实例
console.log(this);
this.name = name;
this.age = age;
}
bark(){
console.log("汪汪汪");
}
}
const dog = new Dog("小黑",2);
const dog2 = new Dog("小白",3);
console.log(dog);
class Person {
firstName:string
lastName:string
constructor(firstName:string,lastName:string) {
this.firstName = firstName
this.lastName = lastName
}
get fullName(){
return this.firstName+"-"+this.lastName
}
set fullName(fullName:string){
let arr = fullName.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
const person:Person = new Person('Errol','King')
console.log(person.fullName)
person.fullName = 'Lily-Rose'
console.log(person.fullName)
(function () {
//定义一个表示狗的类
class Animal {
name:string;
age:number;
constructor(name:string,age:number) {
this.name = name;
this.age = age;
}
sayHello(){
console.log("动物在叫");
}
}
//定义一个表示狗的类,使 Dog 继承 Animal
//Animal是父类,Dog是子类
//子类将拥有父类全部属性和方法
class Dog extends Animal{
//子类覆盖父类方法是方法重写
sayHello(){
console.log("汪汪汪");
}
}
//定义一个表示猫的类
class Cat extends Animal{
sayHello(){
console.log("喵喵喵");
}
}
const dog = new Dog("旺财",2);
console.log(dog);
dog.sayHello();
const cat = new Cat("喵喵喵",1);
console.log(cat);
cat.sayHello();
})();
(function () {
//定义一个表示狗的类
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log("动物在叫");
}
}
class Dog extends Animal {
age: number;
//如果子类写了构造函数,必须在子类构造函数中调用父类构造函数
constructor(name: string, age: number) {
super(name);
this.age = age;
}
sayHello() {
//super表示当前类的父类
super.sayHello();
}
}
const dog = new Dog("旺财", 2);
console.log(dog);
dog.sayHello();
})();
之前我们写了父类 Animal,我们只希望它做父类,不希望它被拿去创建对象,就需要用到抽象类
(function () {
//abstract 开头的是抽象类
//抽象类和其他类没区别,只是不能用来创建对象
//抽象类就是用来被继承的类
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
//定义一个抽象方法
//使用abstract修饰,没有方法体
//抽象方法只能定义在抽象类中,子类必须重写
abstract sayHello():void;
}
class Dog extends Animal {
//如果不重写会报错,强制重写
sayHello() {
console.log("汪汪汪");
}
}
const dog = new Dog("旺财");
console.log(dog);
dog.sayHello();
})();
约束对象结构
之前我们学了类型注解,直接在对象名后写类型注解的坏处:1、代码结构不简洁。2、无法复用类型注解。例如我们看以下代码:
let p1: {
name: string
age: number
sayHello: () => void
} = {
name: '孙悟空',
age: 18,
sayHello() {
console.log("Hello 悟空");
}
}
let p2: {
name: string
age: number
sayHello: () => void
} = {
name: '八戒',
age: 16,
sayHello() {
console.log("Hello 八戒");
}
}
这种情况就用到了接口,接口:为对象的类型注解命名,并为你的代码建立契约来约束对象的结构。我们使用接口修改以上代码:
interface IUser {
name: string
//这个属性可有可无
age?: number
sayHello: () => void
}
let p1: IUser = {
name: '孙悟空',
sayHello() {
console.log("Hello 悟空");
}
}
let p2: IUser = {
name: '八戒',
age: 16,
sayHello() {
console.log("Hello 八戒");
}
}
console.log(p2.name)
p2.name="沙僧"
console.log(p2.name)
现在的属性是可读可写的,如果不想修改可以增加 readonly
interface IUser {
readonly name: string
age: number
sayHello: () => void
}
再尝试修改 name 属性就会报错:
接口在类中的作用
其实接口和抽象类非常相似,区别有两个:
1、抽象类能有抽象方法也可以有普通方法,而接口中的方法都是抽象方法
2、抽象类使用 extends
继承,接口使用implements
实现
(function () {
/********* 例子 1 *********/
//先演示描述一个对象的类型
type myType = {
name: string,
age: number
};
const obj: myType = {
name: "张三",
age: 23
};
//接口在类中的作用就是用来定义一个类的结构
//用来定义一个类中应该有哪些类型和方法
//也可以当作类型声明来使用,和上边的作用一样
interface myInterface {
name: string;
age: number;
}
//可以声明相同的接口
interface myInterface {
gender: string;
}
//那么这个obj2里边就是上边接口的和
const obj2: myInterface = {
name: "李四",
age: 24,
gender: "男"
}
/********* 例子 2 *********/
//接口可以在定义类的时候限制类的结构
// 接口中所有属性都不能有实际值
//接口只定义对象的结构,而不考虑实际值
//在接口中所有的方法都是抽象方法
interface myInter {
name: string;
sayHello(): void
}
//定义一个类实现一个接口
class MyClass implements myInter{
name: string;
constructor(name:string) {
this.name = name;
}
sayHello(): void {
console.log("hello")
}
}
})();
接口也可以继承其他接口,使用关键字 extends
函数类型
定义一个接口,用来做为某个函数的类型使用
interface ISearchFunc {
// 定义一个调用签名
(source: string, subString: string): boolean
}
// 定义一个函数,类型就是上边的接口
const searchString: ISearchFunc = function (source: string, subString: string): boolean {
// 在source中查找substring
return source.search(subString) > -1
}
console.log(searchString('hello,world', 'hello'))//true
(function () {
//定义一个表示人的类
class Person {
//TS可以在属性前添加属性修饰符
//public(共有)
//private(私有)可以添加方法,get、set方法
//protect 只能在当前类和当前类的子类中使用
public name: string;
private age:number;
constructor(name: string,age:number) {
this.name = name;
this.age = age;
}
getAge(){
return this.age;
}
setAge(age:number){
//判断年龄是否合法
if(age>=0){
this.age = age;
}
}
}
const person = new Person("孙悟空",18);
//现在的属性都是在对象中设置的,属性可以被任意修改,这样非常不安全
person.name = "八戒";
//所以可以把属性改为private,然后增加get、set方法
person.setAge(20);
console.log(person);
})();
TS 中设置 getter 方法的方式
(function () {
class Person {
private _name: string;
private _age: number;
constructor(_name: string, _age: number) {
this._name = _name;
this._age = _age;
}
//TS 中getter方法
get name() {
return this._name
}
set name(name: string) {
this._name = name;
}
get age() {
return this._age;
}
set age(age: number) {
if (age > 0) {
this._age = age;
}
}
}
const person = new Person("孙悟空", 18);
person.name = "八戒";
person.age = -22;
console.log(person)
})();
(function () {
class Person {
//可以将属性直接定义在构造函数中
constructor(public name: string, public age: number) {
}
}
const person = new Person("孙悟空", 18);
console.log(person.name);//打印出孙悟空
})();
可选参数:函数在声明的时候,内部的参数使用了?
进行修饰,那么就表示该参数可以传入也可以不用传入,叫可选参数
默认参数:函数在声明的时候,内部的参数有自己的默认值,此时的这个参数就可以叫默认参数
const getFullName = function (firstName: string = "Errol", lastName?: string) {
if(lastName){
return firstName+"-"+lastName
}else {
return firstName
}
}
console.log(getFullName())//Errol
console.log(getFullName('King'))//King
console.log(getFullName('Errol','King'))//Errol-King
剩余参数是放在所有函数声明中所有参数的最后
function showMsg(str:string,...args:string[]) {
console.log(str)//a
console.log(args)//['b', 'c', 'd']
}
showMsg('a','b','c','d')
函数重载:函数名字相同,函数参数及个数不同
我们通过下面的需求来了解重载的意义:现在我们定义一个 add 函数,它可以接收2个 string 类型的参数进行拼接,也可以接收2个 number 类型的参数进行相加
function add(x: string | number, y: string | number) {
if (typeof x === 'string' && typeof y === 'string') {
return x + y;//字符串拼接
} else if (typeof x === 'number' && typeof y === 'number') {
return x + y;//数字相加
}
}
console.log(add('Errol','King'))
console.log(add(1,2))
//如果传入的是非法的数据,ts应该提示错误
console.log(add('Errol',1))
console.log(add(2,'King'))
我们可以看到当传入两个字符串或两个数字的时候是正常的,但如果参数一个传入字符串,一个传入数组会打印 undefined,这不是我们预想的结果,我们希望在编码阶段就给出提示,这就用到了函数重载
我们增加函数重载声明,当我们再传入一个字符串和一个数字时,编辑器会给我们提示
泛型:在定义函数、接口、类的时候不能预先确定要使用的数据的类型,而是在使用函数、接口、类的时候才能确定数据的类型
function f(a: number): number {
return a;
}
//在定义函数或类时,类型不明确时,可以使用泛型
function f1<T>(a: T): T {
return a;
}
//可以直接调用泛型的函数
f1(10);
f1<string>("10");
//可以有多个泛型
function f2<T, K>(a: T, b: K): T {
console.log(a);
return a;
}
f2<number, string>(123, "a");
//可以限制泛型的类型,泛型约束
interface Inter {
length: number;
}
//T extends Inter表示泛型T必须是Inter的实现类(子类)
function f3<T extends Inter>(a: T): number {
return a.length;
}
console.log(f3({length: 10}));//10
console.log(f3("hello"));//5
//泛型类
//在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
class GenericNumber<T> {
zeroValue: T
add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
return x + y
}
let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) {
return x + y
}
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))
内置对象,是TS/JS自带的一些基础对象,提供了TS开发时所需的基础或必要的能力。我们要学内置对象中的属性或方法。怎么学呢?我们可以查文档:JavaScript 标准内置对象