基础类型
布尔值
// boolean 值 true/false
let isBoolean:boolean = false;
isBoolean = true;
数字
// number 除了支持十进制和十六进制字面量,还支持 ECMAScript 2015中引入的二进制和八进制字面量。
let a10: number = 10 // 十进制
let a2: number = 0b1010 // 二进制
let a8: number = 0o12 // 八进制
let a16: number = 0xa // 十六进制
字符串
// 可以使用双引号(")或单引号(')表示字符串
let name:string = "crj"
name = 'crj-ts'
const info = `${name}`
undefined 和 null
// undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null
let und: undefined = undefined
let nul: null = null
数组
// 有两种方式可以定义数组
// 第一种,可以在元素类型后面接上[]
let list:number[] = [1, 2, 3]
// 第二种方式是使用数组泛型,Array<元素类型>
let list:Array<number> = [1, 2, 3]
元组 Tuple
// 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
// 元组类型,在定义数组的时候,类型和数据的个数一开始就已经限定
let t1: [string, number]
t1 = ['crj', 10] // OK
t1 = [10, 'crj'] // Error
// 当访问一个已知索引的元素,会得到正确的类型:
console.log(t1[0].substring(1))
console.log(t1[1].toFixed(2))
枚举 enum
// enum 类型是对 JavaScript 标准数据类型的一个补充
enum Color {A,B,C}
// 枚举数值默认从0开始依次递增
// 根据特定的名称得到对应的枚举数值
let crj: Color = Color.A // 0
console.log(crj, Color.A, Color.B)
// 默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。
enum Color {A=1,B,C}
let crj: Color = Color.A // 1
// 枚举类型提供的一个便利是你可以由枚举的值得到它的名字
enum Color {A=1,B,C}
let crj: string = Color[2]
console.log(crj) // 'B'
any
// 在编程阶段还不清楚类型的变量指定一个类型
let notSure: any = 10
notSure = 'string'
notSure = true// 也可以是个 boolean
let list: any[] = [1, true, 'free']
list[1] = 100
list[2] = 100
void
// void 类型像是与 any 类型相反,它表示没有任何类型。
// 当一个函数没有返回值时,你通常会见到其返回值类型是 void
/* 表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值 */
function fn(): void {
console.log('fn()')
// return undefined
// return null
// return 1 // error
}
// 声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefined 和 null
let unusable: void = undefined
object
// 定义一个函数,参数是object类型,返回值也是object类型
function fn2(obj:object):object {
console.log('fn2()', obj)
return {}
// return undefined
// return null
}
console.log(fn2({name:'crj'))
console.log(fn2(new String('abc')))
// console.log(fn2('abc') // error
console.log(fn2(String))
联合类型
// 联合类型(Union Types)表示取值可以为多种类型中的一种
function toStr(x: number | string) : string {
return x.toString()
}
console.log(toStr(123))
console.log(toStr('123'))
类型断言
// 类型断言有两种形式。 其一是“尖括号”语法, 另一个为 as 语法
// 定义一个一个函数得到一个数字或字符串值的长度
function getLength(x: number | string) {
// return x.length // error
if (x.length) { // error
return x.length
} else {
return x.toString().length
}
}
function getLength(x: number | string) {
if ((<string>x).length) {
return (x as string).length
} else {
return x.toString().length
}
}
console.log(getLength('abcd'), getLength(1234))
类型推断
// TS会在没有明确的指定类型的时候推测出一个类型
// 1. 定义变量时赋值了, 推断为对应的类型.
let b9 = 123 // number
// b9 = 'abc' // error
// 2. 定义变量时没有赋值, 推断为any类型
let b10 // any类型
b10 = 123
b10 = 'abc'
接口
接口
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型
接口: 是对象的状态(属性)和行为(方法)的抽象(描述)
接口类型的对象 【多了或者少了属性是不允许的】【可选属性: ?】【只读属性: readonly】
/*
需求: 创建人的对象, 需要对人的属性进行一定的约束
id是number类型, 必须有, 只读的
name是string类型, 必须有
age 是number类型, 必须有
sex 是string类型, 可以没有
*/
(()=>{
// 定义一个接口
interface InterfacesPerson{
readonly id:number
name:string
age: number
sex?: string
}
// 定义一个对象,该对象的类型就是我定会一的接口 InterfacesPerson
const ren:InterfacesPerson = {
id: 1,
name: 'crj',
age: 18,
// sex: '男' // 可以没有
// xxx: '太丑' // error 没有在接口中定义, 不能有
}
console.log("crj", ren)
// ren.id = 1000 // error 无法分配到 "id" ,因为它是只读属性
// ren.as = "太丑" // error 类型“InterfacesPerson”上不存在属性“as”
})()
函数类型
函数类型,通过接口的方式作为函数类型使用
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。
它就像是一个只有参数列表和返回值类型的函数定义。
参数列表里的每个参数都需要名字和类型。
(()=>{
// 定义一个接口,用来作为某个函数的类型使用
interface InterfacesFun{
// 定义一个调用签名
(source: string, subString: string): boolean
}
// 定义一个函数,该类型就是上面定义的接口
const crjString:InterfacesFun = function (source: string, subString: string): boolean{
return source.search(subString) > -1
}
// 调用函数
console.log(crjString('string,string,string', 'a'))
})()
类类型–类实现接口
类,类型:类的类型可以通过接口来实现
(()=>{
// 定义一个接口
interface InterfaceCRJ{
crj()
}
// 定义一个类,这个类的类型就是上面的接口
class P implements InterfaceCRJ{
crj(){
console.log('====================================');
}
}
// 调用函数
const pp = new P()
console.log(pp.crj())
})()
一个类可以实现多个接口
(()=>{
// 定义一个接口
interface InterfaceCRJ{
crj()
}
// 定义第二个接口
interface InterfaceCRJOne{
crjOne()
}
// 定义一个类,这个类的类型就是上面的InterfaceCRJ 和 InterfaceCRJOne接口
// 一个类同时被多个类进行约束
class P implements InterfaceCRJ,InterfaceCRJOne{
crj(){
console.log('====================================');
}
crjOne(){
console.log('====================================One');
}
}
// 调用函数
const pp = new P()
pp.crj()
pp.crjOne()
})()
接口继承接口
// 类,类型:类的类型可以通过接口来实现
(()=>{
// 定义一个接口
interface InterfaceCRJ{
crj()
}
// 定义第二个接口
interface InterfaceCRJOne{
crjOne()
}
// 接口继承接口
interface InterfaceCRJAndInterfaceCRJOne extends InterfaceCRJ, InterfaceCRJOne {
}
// 定义一个类,这个类的类型就是上面的InterfaceCRJAndInterfaceCRJOne接口
class P implements InterfaceCRJAndInterfaceCRJOne{
crj(){
console.log('====================================');
}
crjOne(){
console.log('====================================One');
}
}
// 调用函数
const pp = new P()
pp.crj()
pp.crjOne()
})()
类
基本使用
类:可以理解为模板,通过模板可以实例话对象
面向对象编程思想
(()=>{
// 类的定义及使用
class P{
// 定义属性
name:string
age:number
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(name:string,age:number = 18){
// 更新对象中的属性数据
this.name = name
this.age = age
}
// 定义实例方法
fun(age:number){
console.log(`crj${this.name},${this.age},${age}`);
}
}
const a = new P(',crjOne',20)
a.fun(30)
})()
继承
继承:类与类之间的关系
继承后类鱼类之间的叫法:A继承了B这个类,那么此时A类叫子类,B类叫基类
子类–> 又叫派生类
基类–> 又叫超类(父类)
(()=>{
// 定义一个类(父类,基类,超类)
class A{
// 定义属性
name:string
age:number
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(name:string,age:number = 18){
// 更新对象中的属性数据
this.name = name
this.age = age
}
// 定义实例方法
fun(age:number){
console.log(`crj${this.name},${this.age},${age}`);
}
}
class B extends A{
constructor(name:string,age:number){
// 调用的是父类中的构造函数,使用的是super
super(name,age)
}
// 重写父类型的方法
fun(){
console.log("我是B类中的fun方法");
super.fun(50)
}
}
const a = new A(',crjA',20)
a.fun(30)
const b = new B(',crjB',20)
b.fun()
})()
多态
多态:父类型的引用指向了子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为
(()=>{
// 定义一个类(父类,基类,超类)
class A{
// 定义属性
name:string
age:number
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(name:string,age:number = 18){
// 更新对象中的属性数据
this.name = name
this.age = age
}
// 定义实例方法
fun(age:number){
console.log(`crj${this.name},${this.age},${age}`);
}
}
// 定义一个子类
class B extends A{
constructor(name:string,age:number){
// 调用的是父类中的构造函数,使用的是super,实现子类中属性的初始化操作
super(name,age)
}
// 实例方法,重写父类中的实例方法
fun(){
console.log("我是B类中的fun方法");
super.fun(10)
}
}
// 定义第二个子类
class C extends A{
constructor(name:string,age:number){
// 调用的是父类中的构造函数,使用的是super,实现子类中属性的初始化操作
super(name,age)
}
// 实例方法,重写父类中的实例方法
fun(){
console.log("我是C类中的fun方法");
super.fun(20)
}
}
const a:A = new A(',crjA',70)
a.fun(30)
const b:B = new B(',crjB',80)
b.fun()
const c:C = new C(',crjC',90)
c.fun()
// 父类和子类的关系:父子关系,此时,父类类型创建子类的对象
const d:A = new B(',crjD',100)
d.fun(60)
console.log("=======================")
function showRun(e:A){
e.fun(120)
}
showRun(b)
showRun(c)
})()
公共,私有与受保护的修饰符
修饰符,类中的成员的修饰符:主要描述类中成员(属性,构造函数,方法)的可访问性
类中的成员都有自己的默认的访问修饰符,public
修饰符名称 | 修饰符描述 | 修饰符理解 |
---|---|---|
public | 公共 | 默认 |
private | 私有 | 当成员被标记成 private 时,它就不能在声明它的类的外部访问 |
protected | 受保护 | protected 修饰符与 private 修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问 |
// public修饰符:类中成员默认修饰符,代表是公共的,任何地方都可以访问类中的成员
(()=>{
class A{
public name:string
public constructor(name:string){
this.name = name
}
public fun(age:number){
console.log(`crj${this.name},${age}`);
}
}
const a:A = new A(',crjA')
console.log(a.name)
a.fun(30)
// private修饰符:类中的成员如果使用private修饰,那么外部是无法访问这个成员数据的,当然,子类中也是无法访问
class B {
private name:string
public constructor(name:string){
this.name = name
}
public fun(age:number){
console.log(`crj${this.name},${age}`);
}
}
class E extends B{
constructor(name:string){
super(name)
}
fun(){
console.log('====================================');
// console.log("继承自B类", this.name); // 这里不可以访问 因为private修饰符
console.log('====================================');
}
}
const b:B = new B(',crjA')
// console.log(b.name) // 不可访问 因为private修饰符
b.fun(30)
// protected修饰符:类中的成员使用protected修饰,那么外部是无法访问这个成员数据,当然,子类中是可以访问的
class C {
protected name:string
public constructor(name:string){
this.name = name
}
public fun(age:number){
console.log(`crj${this.name},${age}`);
}
}
class D extends C{
constructor(name:string){
super(name)
}
fun(){
console.log('====================================');
console.log("继承自C类", this.name); // 这里可以访问 因为protected修饰符
console.log('====================================');
}
}
const c:C = new C(',crjA')
// console.log(c.name) //c 不可访问 因为protected修饰符
c.fun(30)
})()
readonly 修饰符 关键字
readonly 修饰类中的成员属性操作
// 对类中的属性成员进行修饰,修饰后,该属性成员就不能在外部随意修改了
(()=>{
class A {
readonly name:string
constructor(name:string){
// 构造函数中,可以对只读属性成员的数据进行修改
this.name = name
}
fun(){
console.log('crj', this.name);
// 类中的普通方法中,也是不能修改readonly修饰的成员属性值
// aa.name = "李" // error 无法分配到 "name" ,因为它是只读属性。
}
}
// 实例化对象
const aa = new A('张')
console.log(aa)
console.log(aa.name);
// aa.name = "李" // error 无法分配到 "name" ,因为它是只读属性。
// console.log(aa.name);
})()
(()=>{
// readonly 修饰类中的构造函数中的参数(参数属性)
class A {
constructor(readonly name:string){
}
}
// 实例化对象
const aa = new A('张')
console.log(aa.name)
})()
存储器 get set
通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问
class Person {
firstName: string = 'A'
lastName: string = 'B'
get fullName () {
return this.firstName + '-' + this.lastName
}
set fullName (value) {
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
}
const p = new Person()
console.log(p.fullName)
p.firstName = 'C'
p.lastName = 'D'
console.log(p.fullName)
p.fullName = 'E-F'
console.log(p.firstName, p.lastName)
静态成员 static
在类中通过 static 修饰的属性和方法,那么就是静态的属性及静态的方法,也称之为:静态成员
静态成员在使用的时候是通过类名.的这种语法来调用的
(()=>{
// 定义一个类
class A {
name: string = 'A'
// 静态属性
static nameA: string = 'nameA'
constructor(){
// 此时的this是实例对象,nameA是静态属性,不能通过实力对象直接调用静态属性来使用
// this.nameA = nameA // error
}
// 金泰方法
static fun(){
console.log('AA');
}
}
// 实例化对象
const aa:A = new A()
// 通过实例对象调用属性
console.log(aa.name)
// 通过类.静态属性的方式访问该成员数据
console.log(A.nameA)
// 通过类.静态方法的方式访问该成员数据
A.fun()
})()
抽象类 abstract
抽象类:包含抽象方法(抽象方法一般没有任何的具体内容的实现),也可以包含实例方法,抽象类是不能被实例化,为了让子类实例化及实现内部的抽象方法
(()=>{
// 定一个抽象类
abstract class A{
// 抽象方法,抽象方法 不能有具体的实现
abstract fun()
// 实例方法
run(){
console.log('都漂亮');
}
}
// 不能实例化抽象类的对象
// const a:A = new A()
// 定义一个子类(派生类)B
class B extends A{
// 重新实现抽象类中的方法
fun () {
console.log('自己实现');
}
}
const b:B = new B()
b.fun()
// 调用抽象类中的实例方法
b.run()
})()
函数
基本示例
// 函数: 封装了一些重复使用的代码,在需要的时候直接调用即可
(()=>{
// js中
// 函数声明,命名函数
function add(a,b){
return a + b
}
// 函数表达式,匿名函数
let addTwo = function (a,b){
return a + b
}
// TS中
// 函数声明,命名函数
function addTS(a:number,b:number):number{
return a + b
}
// 函数表达式,匿名函数
let addTwoTS = function (a:number,b:number):number{
return a + b
}
// 书写完整函数类型
let add3TS: (a:number,b:number) => number = function (a:number,b:number):number{
return a + b
}
// 可选参数和默认参数
// 可选参数:函数在声明得时候,内部得参数使用了?进行修饰
// 默认参数:函数在声明得时候,内部有自己得默认值
function buildName(firstName: string='A', lastName?: string): string {
if (lastName) {
return firstName + '-' + lastName
} else {
return firstName
}
}
console.log(buildName('C', 'D'))
console.log(buildName('C'))
console.log(buildName())
// 剩余参数
// 剩余参数是放在函数声明的时候所有的参数的最后
function info(x: string, ...args: string[]) {
console.log(x, args)
}
info('abc', 'c', 'b', 'a')
// 函数重载
// 函数重载: 函数名相同, 而形参不同的多个函数
// 在JS中, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的 但在TS中, 与其它面向对象的语言(如Java)就存在此语法
// 重载函数声明
function add (x: string, y: string): string
function add (x: number, y: number): number
// 定义函数实现
function add(x: string | number, y: string | number): string | number {
// 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y
if (typeof x === 'string' && typeof y === 'string') {
return x + y
} else if (typeof x === 'number' && typeof y === 'number') {
return x + y
}
}
console.log(add(1, 2))
console.log(add('a', 'b'))
// console.log(add(1, 'a')) // error
})()
泛型
指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
// 下面创建一个函数, 实现功能: 根据指定的数量 count 和数据 value , 创建一个包含 count 个 value 的数组
// 不用泛型的话,这个函数可能是下面这样:
function createArray(value: any, count: number): any[] {
const arr: any[] = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr1 = createArray(11, 3)
const arr2 = createArray('aa', 3)
console.log(arr1[0].toFixed(), arr2[0].split(''))
// 使用函数泛型
function createArray2 <T> (value: T, count: number) {
const arr: Array<T> = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr3 = createArray2<number>(11, 3)
console.log(arr3[0].toFixed())
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>('aa', 3)
console.log(arr4[0].split(''))
// console.log(arr4[0].toFixed()) // error
多个泛型参数的函数
function swap <K, V> (a: K, b: V): [K, V] {
return [a, b]
}
const result = swap<string, number>('abc', 123)
console.log(result[0].length, result[1].toFixed())
泛型接口
在定义接口时, 为接口中的属性或方法定义泛型类型
在使用接口时, 再指定具体的泛型类型
interface IbaseCRUD <T> {
data: T[]
add: (t: T) => void
getById: (id: number) => T
}
class User {
id?: number; //id主键自增
name: string; //姓名
age: number; //年龄
constructor (name, age) {
this.name = name
this.age = age
}
}
class UserCRUD implements IbaseCRUD <User> {
data: User[] = []
add(user: User): void {
user = {...user, id: Date.now()}
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User {
return this.data.find(item => item.id===id)
}
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)
泛型类
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
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))
泛型约束
// 如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性
// 没有泛型约束
function fn <T>(x: T): void {
// console.log(x.length) // error
}
// 我们可以使用泛型约束来实现
interface Lengthwise {
length: number;
}
// 指定泛型约束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
// 我们需要传入符合约束类型的值,必须包含必须 length 属性:
fn2('abc')
// fn2(123) // error number没有length属性
内置对象
ECMAScript 的内置对象
/* 1. ECMAScript 的内置对象 */
let b: Boolean = new Boolean(1)
let n: Number = new Number(true)
let s: String = new String('abc')
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')
b = true
// let bb: boolean = new Boolean(2) // error
BOM 和 DOM 的内置对象
Window
Document
HTMLElement
DocumentFragment
Event
NodeList
const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {
console.dir(event.target)
})
const fragment: DocumentFragment = document.createDocumentFragment()