孙广东 2016.4.5php
JavaScript现在处处都是。web、server(经过NodeJS)、移动应用(经过各类框架)。所有这些,TypeScript都可以使用,并且可以为JavaScript扩展出面向对象和静态类型的特征。css
TypeScript能让咱们提早使用将来的语言特性。甚至不少其它,好比泛型这样的语言特性。vue
TypeScript代码。终于会编译为地道的JavaScript,兼容一切使用JavaScript的场合。ios
编译过程主要是编译时检查,一点改写,删除类型批注和接口。删除类型批注和接口这个过程称为类型擦除(Type Erasure)。c++
我从网上找到一张很是好的图片用来讲明类型擦除,例如如下。git
咱们后面将具体介绍TypeScript并对照他和其它语言的异同,主要是C#。github
关于TypeScript到底是Compiling 仍是 Transpiling
这个话题很是难说清楚,但是很是有必要在提到TypeScript的时候讲一下。这两个词:编译Compiling,Transpiling有人译做转译。这是一个英文计算机术语。web
通常以为转译是一种特殊的编译,当将一种源码语言编译成第二种源码语言时。就称为转译。sql
当编译一个c#程序时,是由源码语言C#编译为IL。这就不能称为Transpiling。因为他们是全然不一样的东西。typescript
而编译TypeScript程序时,他变成了第二种源码JavaScript,这个就称为Transpiling(转译)。
但无论怎样,Transpiling是Compiling的特例。Transpiling也属于Compiling。
因此TypeScript转译为Javascript。TypeScript编译为JavaScript,都是没有问题的讲法。
TypeScript语言特性
这里高速介绍一下TypeScript的关键语法,比方显示类型批注、类、接口。
尽管C# Java程序猿都很是熟悉面向对象。但TypeScript并不基于C#,因此仍是有所差异的。
TypeScript是静态类型语言,需要编译。拥有编译时类型检查的特性。
编译时类型检查可以确保类型安全。并方便开发更智能的本身主动完毕功能。实际上TypeScript的各类开发工具都作得很是不错。
比方VisualStudio,编写TypeScript文件时,就比编写JavaScript要聪明的多。
这就是静态类型带来的优势。
TypeScript文件
TypeScript文件的扩展名为”.ts”,你可以使用很是多工具编写.ts文件,比方visualstudio。不少其它信息。请看官网http://www.typescriptlang.org/
官网还提供了一个在线编写測试.ts文件的环境http://www.typescriptlang.org/Playground/
一个TypeScript应用包括多个TypeScript代码文件。一个代码文件可以包括多个Class,Class也可以组成模块。
模块的概念和C#中组织类型的namespace比較接近。
运行时。TypeScript编译获得的JavaScript可以经过Html标签<Script>加入网页中,也可以使用其它的模块载入工具,比方NodeJS就内置了模块载入工具。
不使用模块组织依赖的时候,一个TypeScript文件依赖还有一个TypeScript文件,应该加上引用凝视
(这是可选的。通常也不使用命令行编译。大部分图形化工具不加也可以)。
当使用模块载入工具的时候(比方RequireJS。或者NodeJS内置载入工具),代码例如如下:
不知道啥时RequireJS的同窗请自行补课。
当你使用import语句的时候,是有两种模式的,CommonJS 和 AMD模式,他们编译为JavaScript生成的代码不一样,这个依据你使用不一样的载入工具请本身设置TypeScript编译选项。
注:RequireJS使用CommonJS模式,NodeJS使用AMD模式。
类型
TypeScript的基本类型有 string,number,boolean,null和undefined.
因为JavaScript没有整数小数这些区分,因此TypeScript也没有加入,统一使用number。
另外TypeScript加入了一个any类型,当需要动态类型时使用。
这个与C#的动态keyword是类似的。
TypeScript支持数组类型,经过在类型后面加入方括号定义。比方string[].
也可以本身定义类型。在讲到class的时候咱们细说。
类型判断
当右側表达式足以肯定变量的类型时。TypeScript可以本身主动肯定变量的类型。这个特性C#也有。
比方这个代码
假设在VisualStudio中,鼠标悬停,依旧可以看到这三个变量被识别出来是各自不一样的类型。
类型判断还能处理更复杂的类型信息,比方这个代码,无需类型标注就能够得到静态类型的优势。
写完这个代码之后,以后再键入example,再敲入.,也可以得到本身主动完毕提示。.name,.id.collection.
类型判断始终是有局限性的,基本上是依靠右側表达式来判断。
假设声明变量时没有判断出来,过后赋值也判断不出来了。
比方如下的代码。
当声明变量时没法判断出,变量即被标注为 any 类型,any类型全然没有不论什么类型安全方面的保证。固然本身主动完毕功能也没有。
类型批注
TypeScript的类型是设计为批注,而不是定义。是可选的。因此和C#的类型写在前面不一样,类型是以可选的形式写在后面的。
咱们将上面的所有代码都加上类型标注
这些标注的加入和上面本身主动类型判断的结果是同样的。但是阅读代码的人就可以一眼看出。
模块、类型、接口
TypeScript的模块用于代码组织。类似C#的namespace。
一个模块可以包括多个类和接口。可以将类和接口私有化或者导出,导出的意思就是公开,让其它模块可以訪问他们。
TypeScript的class和C#的class意义一样。实际上TypeScript的一个亮点就是他隐藏了JavaScript的原型设计,而是採用了更流行的基于类型的面向对象方式。
你可以扩展其它的class,实现多个接口,加入构造函数。公开属性和方法。这些都和c#的class很是类似。
属性和方法可以使用public 或private訪问修饰符修饰。当在构造函数的參数上使用訪问修饰符的时候,他们会本身主动为该类型加入同名的属性。
请很是当心这个语法。这和很是多语言都不一样
如图中Point的构造函数參数x。y 使用了public 訪问修饰符,因此会本身主动生成Point的成员变量x和y。这是TypeScript的特有语法。
图中的export keyword使类和接口在模块外部可见。实现接口使用implementskeyword,继承类使用extendskeyword。这点和C#直接用一个冒号表达不一样。
当你扩展一个类时。用superkeyword调用基类的方法。用thiskeyword来调用当前类的属性和方法。重写基类的方法是可选的。构造函数必需要调用基类的构造函数,编译器会提醒你的。
模块名称包括点,一个模块可以定义在多个文件里。
模块的名字别太长,訪问的时候打字会比較累。
下图是一个比較累的样例
函数(或者叫功能)的參数
TypeScript的函数參数拥有丰富的特性:可选參数、默认參数、不定參数。
有一些和其它语言的设计不太一样。如下将一一说明。
尽管在设计上,这些參数特性都不是必要的。但是TypeScript的这些地方都有些特殊性。你得了解一下。以备看懂。
可选參数的设计和C#基本一致,符号用? 表示可选參数。可选參数必须出现在必选參数以后。
默认參数仅仅要在定义时给附上值就能够了,并且和c#不一样,TypeScript的默认參数不需要是常量,运行时可解释就能够,后文会有说明
下图是特殊的默认參数
不定參数可以指定随意数量,可以为零。这个设计也和c#类似,仅仅是要加入三个点。
如下就是不定參数的一个典型使用场景
函数重载
Javascript是不一样意重名函数的,TypeScript实现的重载和c#有很是大的不一样。
TypeScript的重载是要把所有的函数签名写出来,写一份实现,并且最后一个函数签名要能包括上面所有的函数签名。
例如如下图:
和C#三个重载的函数就拥有三个函数体不一样,TypeScript的重载事实上全是 overloadedMethod(input:any)这最后一个签名的实现。上面的两个仅仅是两个兼容的签名。但是配合可选參数仍是可以表现出和c#的函数重载类似的调用方式。
函数编写方式免不了要写很是多的if了。
枚举
TypeScript的枚举很是类似C#,你可以指定值。
也可以反过来经过枚举值取到枚举的名字
这里面的细节就再也不赘述了。你可以观察枚举编译为JavaScript之后是什么样子
泛型
对c#程序猿来讲,TypeScript的泛型很是熟悉,基本上是一致的设计。
类型约束
C#使用wherekeyword标记类型约束,TypeScript在尖括号内使用extendskeyword,效果一样。
如下的样例中IExample约束了泛型必须是IMyInterface和他的派生类。
假设像下图这样用的话,就能约束为同一时候继承ClassOne和ClassTwo的类型。很是费解吧。请特别注意。
这是因为本质上TypeScript的类型系统并不那么严格。如下的章节会详解TypeScript的类型系统
你也可以使用泛型做为泛型的类型约束。例如如下
结构类型
C#的类型系统是强制标记的,对象的类型必须显示声明。即便两个类型拥有全然一样的结构,他们也不是一样的类型。
TypeScript的类型系统是结构上的,建筑结构。层次型的。
结构一样的类型就能够以为是同一类型。
如下是C#中的一个样例
ClassA和 ClassB是全然不一样的类型,他们之间是不一样的,必须显示继承接口才干让他们兼容。
而在TypeScript中不是这样,咱们用例如如下的样例来证实。
ClassA ClassB ExampleC 拥有签名一致的函数。因此他们就可以兼容。
TypeScript的结构类型系统意味着你在c#中的观念再也不成立,classname不是关键。
这需要咱们写代码的时候时刻注意。
这玩意会让代码变幻无穷。假设你熟悉C#或者JAVA。这可能会让你困惑。
看如下的样例,不需要classkeyword,也会实实在在的产生类型。
在这个样例中,会产生一个匿名的类型
这个匿名的类型可以让开发工具提供本身主动完毕功能,编译器也会检查。假设你尝试将objA的name赋值一个数值,编译器会检查到告诉你错误。
编译器还会为数组判断类型。
訪问修饰器
TypeScript的訪问修饰器可能会给你一种弱小的感受,的确如此。他仅仅是一个编译时功能。
模块中的一切均为私有,除非加上exportkeyword。没有exportkeyword的类型仅能在模块内实用。
类的内部,一切均为公开,除非加上private keyword。public仅仅是为了看起来意图明白。
TypeScript的訪问修饰器就是这样而已,没有c#的 internal 和 protected这样的修饰器。想要c#类似的功能就放弃吧。
TypeScript特性
内存管理
当你运行TypeScript 程序时,他会被编译为JavaScript程序来运行。
JavaScript的内存管理和C#比較接近。
内存在对象建立时分配,在对象再也不使用时回收。不一样的是垃圾回收机制在JavaScript的世界里没有标准统一的实现,这意味着你的JavaScript程序的内存性能相比C#难以预測。
比方说。在比較早的浏览器上。可能使用的是引用计数垃圾回收机制。当一个对象的引用计数达到0时。将回收内存。这样的垃圾回收机制比較高速和即时。但是当发生循环引用时,引用计数将永远也没法达到零。
比較新的浏览器使用标记与清扫垃圾回收机制来找出不可訪问到的对象,很是大程度上避免了这个问题。这样的垃圾回收机制比較缓慢。但他能避免循环引用致使的内存泄露。
关于两种垃圾回收机制有很是多的资料可以研究。这里仅仅是想告诉你,别相信你的直觉,浏览器会很是不一样。
资源释放
在TypeScript中,一般都不会使用到非托管的资源。Node.JS中多一点。大部分浏览器将底层交互API设计为回调控制,不向你暴露对象。不需要你本身管理非托管资源。比方如下接近传感器的API用法。
如你所见,使用回调并不需要管理不论什么引用。Node.js中有时会碰到需要本身释放的对象。你要保证释放这些对象,不然就会内存泄露。你可以使用 try finally 块。释放代码写在 finally 块中。这样就能保证就算发生不论什么错误。释放代码都会运行。
异常
在TypeScript中,你可以用throwkeyword引起一个异常。
在JavaScript中,throw可以throw不论什么类型的东西。但是再TypeScript中,throw的必须是一个Error对象。
要本身定义异常,可以继承Error类。当你需要一个特定的异常行为或者你但愿catch块可以分辨异常类型时,本身定义异常就会很是实用。
处理异常需要使用try catch语句块。大致上和c#的用法是很是接近的,但是c#支持多个catch块,TpyeScript不可以。你可以用error.name来区分异常类型。
假设需要一些即便发生异常也会调用的代码,你就需要finally语句块了,他们的运行顺序例如如下图
数组
TypeScript 从0.9開始,数组就是可以指定内容的准确类型。
用法是,用类型批注加上方括号。TypeScript会检查加入到数组中的条目的类型,也会判断从数组中检索的条目的类型。
因为TypeScript没有本身的框架库,因此仅仅能使用内置的JavaScript函数和浏览器接口,没有像C#的List<T> 这样的泛型库。
但这并不能阻止咱们本身创造一个。
如下是一个泛型列表类的样例,仅仅是个演示。
上面这个List类演示了不少TypeScript的语言特性和用法。就像C#的List<T>那样。
如下是怎样使用这个List类的样例。
日期与时间
TypeScript中的Date对象是基于JavaScriptDate 对象的,他是由从1970年零点 utc时间開始的毫秒数。
Date对象可以接受各类精度的初始化,例如如下。
你也可以使用RFC和ISO格式的字符串初始化Date对象。固然,毫秒数也可以(从1970年1月1日開始)。
请注意。这不是时间戳。时间戳单位是秒,数字比这个少几个零。
Now
你可以訪问现在的日期与时间,经过方法Date.Now();返回值是毫秒。你假设需要用Date对象去操做。需要用这个值去初始化一个Date对象
Date 的方法
当你有一个Date对象时,你可以用内部方法获取日期的一部分。有两套方法,一套本地,一套UTC。在如下的实例中。你看到咱们把getUTCMonth的值加了1.因为返回值从零開始。因此一月是 0。2 月是 1。等等。
日期的各个部分为年、月、日、小时、分钟、秒、毫秒。所有这些值都可以在获取本地的和 UTC 的。
还可以用 getDay 或 getUTCDay获得星期,从零開始,星期天为 0。星期一为1。
也可以利用各类格式显示日期。例如如下。
事件
TypeScript中的事件是 DOM API(DocumentObject Model),DOM API事件是一套标准的鼠标和键盘交互和对象和窗口事件。如下的事件列表并非详尽无遗。
鼠标事件
事件 |
触发时机 |
onblur |
焦点离开目标组件 |
onclick |
目标组件检測到鼠标单击 |
ondblclick |
目标组件检測到鼠标双击 |
onfocus |
目标组件得到焦点 |
onmousedown |
目标组件检測到鼠标按下 |
onmousemove |
目标组件检測到鼠标指针移动 |
onmouseover |
鼠标指针通过目标组件 |
onmouseout |
鼠标指针离开目标组件 |
onmouseup |
目标组件检測到鼠标button松开 |
键盘事件
事件 |
触发时机 |
onkeydown |
目标组件键盘按下 |
onkeypress |
目标组件键盘按键按下并松开 |
onkeyup |
目标组件键盘松开 |
对象事件
事件 |
触发时机 |
onload |
对象载入(文档或图像等) |
onresize |
对象尺寸改变 |
onscroll |
一个文档滚动 |
onunload |
文档关闭 |
表单事件
事件 |
触发时机 |
onchange |
目标输入框的内容改变 |
onreset |
表单重置(清空) |
onsubmit |
表单提交 |
本身定义事件
TypeScript中本身定义事件和DOM内置事件採用一样的机制公布和侦听。不论什么事件都可以有多个侦听器和多个公布者。如下是一个侦听本身定义事件的样例
如下的类公布本身定义的事件:
运行顺序
事件依照他们被注冊的顺序运行。这个因素很是重要,因为事件处理程序是序列运行。不是同一时候,顺序会影响逻辑。
当你注冊两个事件侦听器,并且第一个运行了5秒钟。
那么第二个就不会运行。一直要等到第一个运行完毕以后才会被运行。假设第一个侦听器出错,第二个仍是会运行。
你可以用setTimeOut时间參数为零的方式去运行你的长时间操做。这至关于多线程。就不会堵住其它的事件运行了。
框架
TypeScript捆绑了那些常用对象和方法。因为Web标准在不断的演变,你可能会常常发现一些新的东西尚未被包括在标准库中。你可以查看你计算机上的TypeScript标准库文件。他在SDK文件夹中,一般多是:
C:\Program Files (86) \MicrosoftSDKs\TypeScript\lib.d.ts
你永远也不该该改动这个文件。假设你需要新的定义。咱们可以继续添加新的定义文件。
如下的是 ClientRectList 在TypeScript库文件里的当前定义。
假设要为 ClientRectList加入一个新的isOrdered属性,仅仅需简单的在你本身的程序中加入如下接口扩展程序,就能够立刻使用。
当它被加入到标准库时,你本身的扩展会引起一个生成错误,到时把它删除了就能够了。
当你建立ClientRectList的实例时,你将可以訪问过去的方法,以及获取新的 isOrdered 属性。
除了这些TypeScript标准库的定义。TypeScript没有捆绑不论什么其它的框架。
在前一章中。咱们谈到了,你可以重建你的各类功能,比方像C#同样,因为TypeScript有着丰富的语言功能。你也可以訪问所有现有的 JavaScript 框架,jQuery、Knockout、Angular和其它数以百计的框架。但是他们都用纯 JavaScript开发,你不会获得加强的开发体验,除非,你有一个匹配的定义文件。幸运的是,有一个项目。他们致力于为所有的JavaScript框架提供TypeScript定义。Definitely Typed 项目,Github地址是:
https://github.com/borisyankov/DefinitelyTyped
建立定义
有时你会想使用JavaScript的库、框架或者工具集。
TypeScript编译器难以理解这些外部的代码,因为他们没有类型信息。
动态定义
在程序中使用外部代码的最简单方法是声明一个any变量。TypeScript编译器赞成any对象调用不论什么方法于属性。
这会让你经过编译,但是没有本身主动完毕和类型检查。
在此演示样例中的特殊keyword是declare。这会告诉编译器,这个变量是外部的,这个仅仅是编译时的keyword。编译为JavaScript时会被擦除。
类型定义
为了使用外部JS代码能获取更佳的开发体验,你需要提供更全面的定义。
可以声明变量、 模块、 类和函数,定义外部代码的类型信息。
Declarekeyword仅仅需要在定义的开头使用一次。
假设但愿外部代码可以由TypeScript扩展,定义为class,不然。定义为interface。
这两种定义方法的惟一差异是是否能继承扩展。这两种状况下,类型信息都仅为编译时使用。编译为JavaScript后都会进行类型擦除。
很是多时候,定义会组成一系列接口。组合成很是大的一幅图景。你甚至可以为TypeScript不能实现的JavaScript方法建立定义。
好比。它是例如如下图这样作:
在此演示样例中有一个名为move的难以想象的属性,他可以做为函数使用。这样一来,咱们就可以声明差点儿不论什么的JavaScript代码, jQuery、Knockout、Angular、RequireJS 和其它的老的JavaScript代码,让你能在TypeScript中使用它们而没必要重写。
一些实用的小技巧
获取运行时类型
假设在运行时,想要获得类的名称。在C#中有反射这样的方法。但TypeScript没有明显的内置方法。
静态方法 getType,检查编译后的 JavaScript 函数。而后提取它的名字,这就是TypeScript中的类的名称。
这样的技术的限制是你不能得到包括类和模块的完整名称,这意味着:
MyModule.Example和 SomeOtherModule.Example 和没包装的叫作Example的类,它们所有的返回字符串均为Example。
扩展原生对象
TypeScript内置的定义文件描写叙述了JavaScript原生对象和函数。
你可以在lib.d.ts库文件里查看它们。
你会注意到大部分都是使用interface定义的。不但愿你继承它们。
但咱们仍是可以扩展它们的。
好比。如下是给NodeList对象添加onclick事件。
但是这个代码会被TypeScript编译器警告,告诉你,NodeList对象没有onclick函数。
为了解决此为题,你需要在你本身的代码中加入定义代码。
这个接口声明可以写在不论什么引用的ts文件里。