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
装饰器执行顺序:
属性装饰器>方法装饰器>方法参数装饰器(从后到前)>类装饰器(从后到前)