TypeScript从入门到入土
前排提示:由于本文作者的TypeScript是自学的,所以对某些内容的理解可能并不准确,本文的目的是以尽可能通俗易懂的方式对TypeScript的一些基础用法进行说明,请不要过于较真。毕竟真的较起真来就不通俗易懂了,对吧?
考虑到发布平台,本文建立在您已有C/C++语言基础的条件上进行说明,如果没有建议先去了解一下再来阅读。
为什么要学习TypeScript?
又到了每日自(瞎)夸(扯)时间。
首先当然是TypeScript的友好性。
如果您已经掌握了C/C++的基本语法且会一点英语,比如认识function啥的,您将能轻松看懂下面一段TypeScript代码:
1 | function qpow(a:number,b:number){ |
作为解释型语言,它与C++更为相似,相较于Python对OIer应当更为友好。
以及,作为JavaScript的简化版(写法意义上),TypeScript支持很多JavaScript的特性,但写起来十分简单。JavaScript,狗都不写
例如,TypeScript的异步写法是前所未有的简单。
TypeScript语法入门
0.前置芝士
在TypeScript中,分号可写可不写。
TypeScript的注释表达方式与C++完全一致,即
//
表达单行注释,/**/
表达多行注释。TypeScript中有一个基本概念:“对象”,可以粗略理解为C++中的一个class。
1.变量定义
1.单变量定义
TypeScript中有以下几种常用变量类型:
类型 | 标识符 |
---|---|
布尔值 | boolean |
数字 | number |
字符串 | string |
任意 | any |
注意,数字类型包含了所有数字(即整数和实数均可以用number定义)。
具体的定义方式有点像Pascal,比如:
1 | var score:number=114514 |
事实上,如果将var
改为let
,不会有任何实际影响。(反正我写到现在没发现什么区别?)
至于var
或者let
的具体意义,事实上是等效于C++中的auto
的。比如,上面的一段代码也可以这么写:
1 | var score=114514 |
不会有任何问题。
对于any
类型,就是类似于Python的变量类型,可以中途更改其类型。
例如,你可以干这种事:
1 | var score:any='114514' |
不会有任何问题。
2.常量定义
如果想定义一个常量,与C++类似,直接将var
或let
换成const
即可。
如:
1 | const now=1919810 |
3.数组定义
这里有两种方法。一种是利用Array
关键字构造:
1 | let a:Array<number>=[1,2,3] |
当然也可以利用自动识别的特性直接写:
1 | let a=[1,2,3] |
4.元组定义
这里的元组与C++中同样类似。
1 | let tup:[number,string]=[114514,"jianan"] |
注意,如果这里直接写成let tup=[114514,"jianan"]
,会被等效成let tup:any=[114514,"jianan"]
。所以如果真的需要一个绑定式的元组,建议写成完整形式。
Tips:在Typescript中,表示字符串可以用’’,``或者””,具体区别会在下文详细讨论。
2.基础运算
与C++完全一致。甚至连运算符都是一样的。
3.字符串运算
这里之所以把字符串单独拎出来细说,是因为TypeScript的字符串操作真的太方便了。
除了基础的用+
将两个字符串拼在一起外,还可以直接把字符串和数字拼在一起:
1 | let a='a'+1 |
那如果是想把数字转成对应的字符再拼起来呢?
这需要用到String
对象中的fromCharCode
函数:
1 | let a='a'+String.fromCharCode(65) |
至于单引号与双引号的区别,这完全是个人习惯,基本上是可以混用的。而单引号与双引号均可表示字符串的好处就是,在字符串内容中含有双引号的时候,用单引号括起来就不需要加转义符;同样,含有单引号的时候,用双引号括起来也不需要转义符。当然如果你非要用双引号括含有双引号的字符串然后加转义符,我也拦不住你对吧,只不过你的代码会很难看而已。
有没有什么简单的方法呢?
有。这就要请出我们的万能字符串标识符:`
如果你用单引号或者双引号表达一个多行的字符串,你将不得不这么写:
1 | 'Hello,\n everyone.\n' |
但是如果换成`呢?
1 | `Hello,\n |
是的,`所括起的字符串不会因为换行被截断。这在在TypeScript嵌入HTML的情况中非常好用。在两个``之间,你可以按正常的方式直接书写HTML,而不用实时考虑缩进之类的问题。
而`一个更强大的功能就是直接插入式的字符串编辑:
比如,现在你有一个变量score
,你想将他放在一个句子中输出,如果用单引号,你只能这么写:
1 | console.log('Yes your score is '+score) |
但是如果用`呢?
1 | console.log(`Yes your score is ${score}`) |
在书写一个较为复杂的输出格式的时候,使用`和${}添加变量会简单许多。
在尝试将字符串转为数字时,同样有几种做法:
1 | var a=parseInt('1234')//a->1234 |
如果转换的内容不为十进制数字,则以上所有方式均会返回一个NaN
对象。
2.条件语句
与C++完全一致。值得一提的是,在TypeScript中,你可以使用===
和!==
来代替==
和!=
,没有什么区别。
3.循环
与C++类似,TypeScript支持while
和for
两种循环,写法与C++类似。
比如,你可以这么写:
1 | let a=[1,2,3] |
4.函数定义
1.function式定义
与JavaScript的写法基本一致,即一个函数可以套用这样的模板:
1 | function name(params:type) { |
2.const式定义
这种写法其实有些类似于C++里的#define。
而在TypeScript中,=>
标识符可以表示对函数的定义,具体的,=>
前的内容为输入的变量,=>
后的内容为函数内容。需要注意的是,如果输入的变量不止一个,则需要用()
括起来。
比如:
1 | const Out=(keyword:string,score:number)=>`The key is ${keyword} and your score is ${score}.` |
5.异步编程
1.什么是异步编程?
当一个异步过程调用发出后,调用者不能立刻得到结果。基于事件机制,实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。比如,你现在要有一批数据要大数据要入库,你又不想一边入库一边等待返回结果,你可以用异步,将大数据推入一个队列,然后另外一个线程来操作这个队列里面的数据入库,入完了,就通知一下主线程。这段时间你的主线程可以做任何事。
——百度百科
学废了?
换个通俗易懂的说法:
想象一下你是网上阅卷的系统。你把第16题分配给甲改了之后,显然并不想等甲改完了再去讨论第17题分给谁。于是你把16题丢给了甲,然后没等甲改完就把17题丢给了乙。甲:囸,我不想改16题啊最终甲改完了16题,告诉你得分;乙也改完了17题,告诉了你得分。
这就是异步的处理模式。如果用同步模式处理,你将不得不等甲改完了16题才能让乙去改17题。
2.异步编程有什么作用?
如大家在刚才的例子中所看到的,异步编程能以单线程的形式,实现类似多线程的效果。另外,在某些场合中(比如刚才的改卷),有些函数在调用后并不能立刻给出结果(或者是结果根本无关紧要),这时候为了避免等待函数给出结果的时间里主程序无事可做的情况,我们选择了异步编程。
3.异步编程怎么写?
大多数编程语言的默认编程模式都是同步的。但是在你想要写成异步的模式的时候,如果你使用C++或者Python,你将不得不写成多线程的模式,而TypeScript呢?
直接在函数前加上一个async
标识符即可。
还是那个快速幂:
1 | async function ksm(a:number,b:number) { |
就行了。
但如果想弄懂TypeScript的异步编程到底是怎么实现的,我们需要先引入两个概念:Promise对象和callback回调函数。
顾名思义,Promise对象就是一个“承诺”,也就是异步编程中的核心:你调用了我,我承诺会给你一个返回值,但不是现在。正因为此,所以Promise对象默认类型是any,即不确定的。如果要想让它确定,就必须在函数中指定他的类型。
而callback回调函数则是保证你没有写了一句废话的函数。由于异步编程的特性,主进程并不会等它调用的函数给出返回值;于是它调用的函数就必须在运行完毕后主动回来找主进程,从而把返回值交给主进程。
而回调函数就完成了这个任务。
因此,如果想让我们的快速幂不是写了一个废话,从更为规范的角度,它应该写成这样:
1 | async function ksm(a:number,b:number):Promise<number>{ |
然后我们调用它时,需要用.then
标识符来指定获得了函数返回结果的时候做什么事:
1 | ksm(2,10).then(res=>{ |
.then
引导的回调函数往往会用const式进行定义,具体格式就是函数返回的所有值会按顺序分配给.then
里变量区域的各个变量,后续函数内容应当对变量区域新定义的变量操作。
如果是函数出错了怎么办?
这在工程开发中相当常见。一般出现错误的时候,我们会使用throw
语句跑出错误:
1 | throw(err) |
而回调函数同样也具有解决报错的功能。
我们使用.catch
标识符表达获得意外返回值的时候做什么:
1 | ksm(2,10).then(res=>{ |
如果不写.catch
回调函数,则当ksm
函数出错时,程序将因为不知道应该干什么而报错。
6.TypeScript中的IO
与JavaScript类似,我们用console
对象来进行标准的输入输出。
console.log
和Python中的print
完全类似。
1 | console.log('a','b','c')//输出a b c |
而console.info
,console.warn
和console.error
与console.log
的区别就在于他们都会在输出的行首加上一个特殊的符号。具体如下表:
函数 | 符号 |
---|---|
console.info |
蓝色i标识 |
console.warning |
黄色三角形标识 |
console.error |
红色叉标识 |
而文件IO需要用到fs库,这里就不展开说了。
用TypeScript写个脚本
用TypeScript写个脚本有什么好处?
当然是方便。
得益于TypeScript极为方便的异步编程,我们可以轻松的用单线程实现这样的效果(50cookies,30s cd):