天天看点

ts:学习总结TS第一个TS文件

TS

TypeScript是什么

TypeScript是JavaScript的超集。(比JS多了一个类型系统)

与JS相比的优势:

.类型化思维方式,使得开发更加严谨,提前发现错误,减少改Bug时间

.类型系统提高了代码可读性,并使维护和重构代码更加容易

.补充了接口、枚举等开发大型应用时JS缺失的功能

安装解析TS的工具包

-在vscode终端

-命令:

npm install -g typescript
           

typescript:就是用来解析ts的工具包,提供了tsc命令,实现了TS->JS的转化

第一个TS文件

创建第一个ts文件

-创建一个文件,以.ts结尾

-写入代码

console.log('hello ts')
           

在终端中运行

tsc .\hello.ts
           

**报错:tsc:无法加载文件,是因为在此系统禁止运行脚本

解决:以管理员身份打开powershell,输入以下命令,再敲Y,就可以了

set-ExecutionPolicy RemoteSigned
           

执行完成后生成.js文件

-执行js文件

node hello.js
           

在窗口中打印出hello ts

简化执行ts步骤

使用ts-node包,“直接”在Node.js中执行ts代码

-安装ts-node

npm i -g ts-node
           

-可以直接用ts-node 命令运行ts文件

注释

-单行注释 // 快捷键:ctrl+/

-多行注释 快捷键:shift+alt+a

输出语句

作用:在终端打印信息

变量

变量:存储数据的容器,并且是可以变化的

变量的使用

.声明变量并制定类型

let age:number
           

.给变量赋值

age=19
           

或:声明变量的同时就赋值

let age:number = 19
           

类型注解

类型注解:是一种为变量添加类型约束的方式。约定了什么类型,就只能给变量赋什么类型的值。

命名规则

变量名称只能出现:数字、字母、下划线、$,且不能以数字开头

**命名规范:驼峰命名法:首字母小写,后面每个单词首字母大写

数据类型

-原始数据类型(基本数据类型)

number/string/boolean

/undefined:声明但未赋值的变量

/null:声明了变量并且赋值null,值为null

-对象类型(复杂数据类型)

-数组类型(array)

1.第一种

var arr:number[] = [11,22,33]
           

2.第二种定义数组的方式

var arr:Array<number>=[11,22,33]
           

3.第三种

var arr3:any[] = [11,22,33]
           

-元祖类型(tuple) :属于数组的一种, 可以给数组中任意位置指定类型

let arr:[number,string] = [123,'this is ts']
           

-枚举类型(enum)

enum Flag (success=1,error=2)
let f:Flag = Flag.error;
console.log(f)  //如果没有给这个值赋值,则打印的是下标
           
-任意类型(any) 变量可以为任意类型
      
var num:any =123;
           

用处:

var oBox:any = document,getElementById('box')
oBox.style.color='red'
           

-null和undefined(never类型)

var num:number | null |undefined   //一个元素可以为多个类型
           

方法没有返回值

function run():void{
  console.log('run')
}
           

-never类型:是其他类型(包括null和undefined)的子类型,代表从不会出现的值

声明never的变量只能被never类型所赋值

var a:undefined;
a = undefined;
           

运算符

算术运算符 + - * /

**加号+还可以实现字符串拼接

赋值运算符

递增、递减运算符

比较运算符

逻辑运算符

函数

1.函数声明法

function run():string{
  return 'run';
}
           

2.匿名函数法

var fun2 = function():number{
    return 123;
}
           

3.ts中定义方法传参 (函数参数指定类型,函数也要指定类型)

function getInfo(name:string,age:number):string{
  return `${name}---${age}`;
}
           

方法可选参数

es5里面方法的实参和形参可以不一样,但是ts中必须一样,如果不一样就需要配置可选参数

在参数后面加一个问号表示参数是可选的
function getInfo(name:string,age?:number):string{
  if(age){
    return `${name}---${age}`
  }else{
    return `${name}---年龄保密`;
  }
}
alert(getInfo('zhangsan'))
           

默认参数

es5里面没设置默认参数,es6中可以有可选参数

可选参数必须配置到参数的最后面

function getInfo(name:string,age:number=20):string{
  if(age){
    return `${name}---${age}`
  }else{
    return `${name}---年龄保密`;
  }
}
           

剩余参数

三点运算符,接收新传递过来的值

原来是这样的:

function sum(a:number,b:number,c:number,d:number):number{
  return a+b+c+d;
}
           

可以用一个result来存传递过来的参数

function sum(...result:number[]):number{
  var sum = 0;
  for(var i=0;i<result.length;i++){
    sum+=result[i];
  }
  return sum;
}
alert(sum(1,2,3,4))
           

或者:

function sum(a:number,...result:number[]):number{
  var sum = 0;
  for(var i=0;i<result.length;i++){
    sum+=result[i];
  }
  return sum;
}
alert(sum(1,2,3,4))
           

就把第一个参数赋值给a

函数重载

通过为同一个函数提供多个函数类型定义来实现多种功能的目的

function getInfo(name:string):string;
function getInfo(name:number):number;
function getInfo(name:any):any{
  if(typeof str==='string'){
    return '我叫:'+str;
  }else{
    return '我的年龄是'+str;
  }
}
//如果console.log(true) 则会报错,为了规范写法,规定传入的值得类型
           

箭头函数

主要在setTimeout中,把参数从functiong改为箭头函数

箭头函数中的this指向上下文

es中类的定义

1.最简单的类

function Person(){
  this.name='张三';
  this.age=20;
  this.run()=function(){
    alert('在运动');
  }
}
var p = new Person();
alert(p.name);
           

2.构造函数和原型链里面增加方法

function Person(){
  this.name='张三';
  this.age=20;
  this.run()=function(){
    alert('在运动');
  }
}
Person.prototype.sex='男';  //直接在prototype上挂载属性和方法
Person.prototype.work=function(){
  alert('在工作')
}
var p = new Person();
alert(p.name);
           
原型链上的属性会被多个实例共享,构造函数不会

静态方法

Person.getInfo=function(){}
           

Web类 继承Person类 原型链+对象冒充的组合继承模式

function Web(){
  Person.call(this);  //对象冒充实现继承
}
var w = new Web();
w.run();  //正确
w.work(); //错误
           
对象冒充可以继承构造函数里面的属性和方法,但是不能继承原型链上面的属性和方法

Web类 继承Person类 原型链+对象冒充的组合继承模式

Web.prototype = new Person()  //原型链实现继承
var w = new Web();
w.run();  //正确
w.work();  //正确
           

原型链实现继承的问题

function Person(name,age){
  this.name='张三';
  this.age=20;
  this.run()=function(){
    alert(this.name+'在运动');
  }
}
Person.prototype.sex='男';  //直接在prototype上挂载属性和方法
Person.prototype.work=function(){
  alert(this.name+'在工作')
}
function Web(name,age){
  
}
Web.prototype = new Person();
var w = new Web('赵四',20);
w.run()  //输出undefined在运动
           
实例化子类的时候没法给父类传参
function Web(name,age){
  	Person.call(this,name,age) //对象冒充继承,实例化子类可以给父类传参
}
Web.prototype = new Person();
var w = new Web('赵四',20);
w.run()  //输出undefined在运动
           

原型链+对象冒充的另一种方式

function Web(name,age){
  	Person.call(this,name,age) //对象冒充继承,实例化子类可以给父类传参
}
Web.prototype = Person.prototype;
var w = new Web('赵四',20);
w.run()  //输出undefined在运动
           

typescript中的类

class Person{
  name:string;
  constructor(n:string){  //构造函数  实例化类的时候触发的方法
    this.name=n;
  }
  run():void{
    alert(this.name);
  }
}
var p = new Person('张三');
p.run();
           

实例化的时候先将'张三'赋值给了constructor中的n,触发构造函数,将n赋值给了name属性,在run方法中就可以拿到name的值,打印出name的值。

定义类,定义类的方法,获取属性,改变属性的值

class Person{
  name:string;
  constructor(n:string){  //构造函数  实例化类的时候触发的方法
    this.name=n;
  }
  getName():string{
    return this.name;
  }
  setName(name:string):void{
    this.name = name;
  }
}
var p = new Person('张三');
p.getName();
p.setName('李四');
           

ts中的继承

还是主要通过extends和super实现

class Person{
  name:string;
  constructor(name:string){
    this.name = name;
  }
  run():string{
    return `$(this.name)在运动`
  }
}
class Web extends Person{   
  constructor(name:string){
    super(name);
  }
}
var w = new Web('李四');
w.run();
           
不仅要继承类,还要用super继承里面的参数

思路:new w的时候,把’李四‘传给Web中的constructor,然后通过super调用父类的构造函数,将name传给Person中的constructor,父类接收到name为'李四',然后w在调用run方法的时候打印出父类的name李四。

super相当于初始化父类的构造函数

ts中继承的探讨:父类的方法和子类的方法一致

一致的时候先找子类的方法,子类没有这种方法再去找父类

类里面的修饰符

ts里面定义属性的时候提供了三种修饰符

public:共有,在类、子类、类外面都可以访问(默认)

protected:保护类型,在类、子类里面可以访问,在类外面无法访问

private:私有,在类里面可以访问,子类、类外面无法访问

静态属性和方法

es5中的静态方法

function Person(){
  this.run1 = function(){  //实例方法
  }
}
Person.name='哈哈哈';
Person.run2 = function() //静态方法
function $(element){
  return new Base(element);
}
$.get=function(){};
function Base(element){
  this.element=获取dom节点;
  this.css=function(arr,value){   //实例方法
    this.element.style.arr = value;
  }
}
$('#box').css('color','red');  
$.get('url',function(){})   //静态方法
           

$('#box')相当于new Base(element),通过函数传入dom节点,调用Base中的实例设置css 样式

ts中的静态方法

class Person{
  	public name:string;
  	public age:number = 20;
  	static sex ='男';
  	constructor(name:string){
		this.name = name;
	}
	run(){  //实例方法,实例化的时候调用的方法
      alert(`${this.name}在运动`)
	}
	work(){
      alert(`$(this.name)在工作`)
	}
	static print(){  //加上static关键字为静态方法
      alert('print方法')
      alert('print方法'+this.age)//错误,静态方法无法直接调用类里面的属性
      alert('print方法'+Person.sex) //正确,换成静态的属性
	}
}
Person.print();
           

多态

多态:父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现

多台属于继承

class Animal{
  name:string;
  constructor(name:string){
    this.name=name;
  }
  ear(){  //不知道吃的是什么
    console.log('吃的方法');
  }
}
class Dog extends Animal{
  constructor(name:string){
    super(name);
  }
  ear(){
    return this.name+'吃粮食';
  }
}
class Cat extends Animal{
  constructor(name:string){
    eat(){
      return this.name+'吃老鼠'
    }
  }
}
           

抽象类

用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现,并且必须在派生类中实现

抽象方法只能放在抽象类中

抽象类和抽象方法用来定义标准,Animal这个类要求它的子类必须包含eat方法

abstract class Animal{
	public name:string;
	constructor(name:string){
      this.name = name;
	}
  	abstract eat():any;
  	run(){};//不是抽象方法子类可以不实现
}
var a = new Animal() //错误,抽象类主要给子类提供一个基类

class Dog extends Animal{
  constructor(name:string){
    super(name);
  }
  eat(){   //在抽象类的子类中必须实现抽象类中定义的方法
    console.log(this.name+'吃粮食')
  }
}
var d = new Cat('小花猫')
           

接口

作用:限制和规范。不关心类的内部状态数据,也不关心这些类里方法的实现细节,只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。

定义:

interface 接口名称{

​ 属性:类型;

}
           

接口的可选属性 属性?:类型

函数类型接口

作用:对方法传入的参数以及返回值进行约束,也可以进行批量约束

加密的函数类型接口

interface encrypt{

(key:string,value:string):string;

}

var md5:encrypt=funciont(key:string,value:string):string{

return key+value;

}
           

可索引接口

数组、对象的约束(不常用)

interface UserObj{
  [index:string]:string
}
var arr:UserObj={name:'张三'}
           

类类型接口

对类的约束 和抽象类有点类似

interface Animal{
  name:string;
  eat(str:string):void;
}
class Dog implements Animals{
  name:string;
  constructor(name:string){
    this.name = name;
  }
  eat(){
	console.log('吃粮食')
}
}
           
*在Dog中的eat可以不传参数,但是不能没有eat()方法,传参数的话必须得传stringL类型

接口扩展 :接口可以继承接口

interface Animal{
	eat():void;
}
interface Person extends Animal{
  word():void;
}
class Web implements Person{
  public name:string;
  constructor(name:string){
    this.name = name;
  }
  eat(){
    console.log(this.name+'喜欢吃馒头');
  }
  work(){
    console.log(this.name+'写代码');
  }
}
           
*要实现父类接口定义的属性和方法

泛型

作用:解决接口方法的复用性,一级对不特定数据类型的支持(类型校验)

引入:

如下是一个返回字符串类型的函数和一个返回数字类型的函数

function getData(value:string):string{
  return value;
}
functiong getData(value:number):number{
  return value;
}
           

这样会很冗余,虽然可以写成如下的函数

function getData(value:any):any{
  return value;
}
           

但是传入的参数类型和返回的参数类型不一致,没有类型校验

因此出现了泛型:可以支持不特定的数据类型,要求:传入的参数和返回的参数一致

function getData<T>(value:T):T{
  return value;
}
           
T表示泛型,具体什么类型是调用这个方法的时候决定的

调用:

getData<string>('1234321')      

错误的调用:

getData<number>('1234321')      

注意:在这里传入什么返回什么,也就是参数中的value和return中的value应该一致,否则应该写成如下代码

function getData<T>(value:T):any{
	return value;
}
           

泛型类

比如有个最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现

class MinClas<T>{
	public list:T[]=[];
	add(value:T):void{
      this.list.push(value);
	}
	min():T{
      var minNum=this.list[0];
      for(var i=0;i<this.list.length;i++){
        if(minNum>this.list[i]){
          minNum = this.list[i];
        }
      }
      return minNum;
	}
}
var m1 = new MinClas<number>();  //实例化类 并制定了类的T代表的是类型number
m1.add(1);
m1.add(3);
m1.add(2);
console.log(m1.min());  //输出1
           

泛型接口

比如有一个函数类型接口

interface ConfigFn{
  (value1:string,value2:string):string;
}
var setData:ConfigFn=function(value1:string,value2:string):string{
  return value1+value2;
}
setData('name:','张三');
           

泛型接口方法一:

interface ConfigFn{
  <T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
  return value;
}
getData<string>('张三');
           

泛型接口方法二:

interface ConfigFn<T>{
  (value:T):T;
}
function getData<T>(value:T):T{
  return value;
}
var myGetData:ConfigFn<string> = getData;
myGetData('20');
           

应用:操作数据库的泛型类

class MysqlDb<T>{
  add(info:T):boolean{
    console.log(info);
    return true;
  }
}
//定义User类,和数据库进行映射
class User{
  password:string | undefined;
  username:string | undefined;
}
var u = new User();
u.username='张三';
u.password='123123';
var Db = new MysqlDb<User>();
Db.add(u);
           

模块

可以把一些公共的功能单独抽离成一个文件作为一个模块,模块里面的变量、函数、类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类),我们需要通过export暴露模块里面的数据(变量、函数、类),暴露后我们通过import引入模块就可以使用模块里面暴露的数据(变量、函数、类)

导出:export xxx();

导入:import {xxx as xxxxx} from 'where'

或者用export default 默认导出:每个模块都可以有一个default导出,默认导出使用default关键字标记,并且一个模块只能够有一个default导出,需要使用一种特殊的导入形式

导出:export default xxx();

导入:import xxx from 'where'

命名空间

内部模块,主要用于组织代码,避免命名冲突

模块:ts的外部模块的简称,侧重代码的复用,一个模块可能会有多个命名空间

装饰器

是一种特殊类型的声明,能够被附加到类声明,方法,属性或参数上,可以修改类的行为,通俗来说,装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能

常见装饰器:类装饰器、属性装饰器、方法装饰器、参数装饰器

装饰器写法:普通装饰器(无法传参)、装饰器工厂(可传参)

装饰器后面不需要加分号

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明),类装饰器应用于类构造函数,可以用来监视、修改或替换类定义

1.扩展类的方法

普通装饰器

function logClass(params:any){
  console.log(params);  //这里的params就是当前类
  //给当前类追加属性
  params.prototype.apiUrl = '动态扩展的属性';
  params.prototype.run=function(){
    console.log('我是一个方法');
  }
}
@logClass()
class HttpClient{
  constructor(){
    
  }
  getData(){
    
  }
}
           

装饰器工厂(传递参数)

function logClass(params:any){
//这里的paramas是'hello'  target是当前类
  return function(target:any){
    console.log(target);
    console.log(params);
    target.prototype.apiUrl = params;
  }
}
@logClass('hello')
class HttpClient{
  constructor(){
    
  }
  getData(){
    
  }
}
           

在装饰器工厂中相当于将'hello'传给了params,将class HttpClient类传给了target

2.重构构造函数

类装饰器表达式会在运行时当做函数被调用,类的构造函数作为其唯一的参数,如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明

function logClass(target:any){
  console.log(target);
  return class extends target{
    apiUrl:any = '我是修改后的数据'
    getData(){
      this.apiUrl=this.apiUrl +'----';
      console.log(this.apiUrl);
    }
  }
}
@logClass
class HttpClient{
  public apiUrl:string | undefined;
  constructor(){
    this.apiUrl = '我是构造函数里面的apiUrl';
  }
  getData(){
    console.log(this.apiUrl);
  }
}
var http=new HttpClient();
http.getData();
//打印出 我是修改后的数据
           

属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数

1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象

2.成员的名字

function logProperty(params:any){
  return function(target:any,attr:any){
    console.log(target);
    console.log(attr);
    target[attr]=params;  //修改为定义装饰器时传入的值
  }
}
@logClass('xxx')   //装饰器工厂
class HttpClient{
  @logProperty('http://baidu.com');  //将url修改为http://baidu.com
  public url:any | undefined;
  constructor(){
    
  }
  getData(){
    console.log(this.url);
  }
}
var http=new HttpClient();
http:getData();
//打印出 http://baidu.com
           

方法装饰器

会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法的定义

方法装饰会在运行时传入下列3个参数:

方法装饰器一:在方法装饰器中修改当前的属性和方法

1.对于静态成员来说是类的构造函数,对于实力成员是类的原型对象

2.成员的名字

3.成员的属性描述符

function get(params:any){
  return function(target:any,methodName:any,dec:any){
    console.log(target);
    console.log(methodName);
    console.log(dec);
    target.apiUrl='xxx';
    target.run=function(){
      console.log('run');
    }
  }
}
class HttpClient{
  public url:any | undefined;
  constructor(){
    
  }
  @get('http://123123')
  getData(){
    console.log(this.url);
  }
}
var http:any = new HttpClient();
http.run();
           

方法装饰器二:修改当前方法中的参数

function get(params:any){
  return function(target:any,methodName:any,dec:any){
    console.log(target);
    console.log(methodName);
    console.log(dec.value);  //打印出当前的方法
    //将传入的参数全改为字符串类型
    desc.value=function(...args:any[]){
      arts = args.map((value)=>{
        args = args.map(value)=>{
          return String(value);
        }
        oMethod.apply(this.args)
      })
    }
  }
}
class HttpClient{
  public url:any | undefined;
  constructor(){
    
  }
  @get('http://123123')
  getData(...args:any[]){
    console.log(args);
    console.log('我是getData里面的方法')
  }
}
var http:any = new HttpClient();
http.getData(123,'xxx');
//打印出 ['123','xxx']
           

方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:

1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象

2、参数的名字

3、参数在函数参数列表中的索引

function logParams(params:any){
	return function(target:any,paramsName:any,paramsIndex:any){
      console.log(target);
      console.log(paramsName);
      console.log(paramsIndex);
	}
}
class HttpClient{
  public url:any | undefined;
  constructor(){
  }
  getData(@logParams('asdf'),asdf:any){
    console.log('我是getData里面的方法')
  }
}
var http = new HttpClient();
http.getData
           

装饰器执行顺序:

属性装饰器>方法装饰器>方法参数装饰器(从后到前)>类装饰器(从后到前)

继续阅读