前言
typescript(以下简称ts)是微软开发、google(Angular2)支持的 js超集(增加面向对象编程的特性和某些ES6语法特性),任何js都可以不经修改的在ts环境下运行。很多著名的项目都已经将ts作为了开发语言,例如,我国的HTML5游戏引擎Egret、我国的前端样式库Ant-desgin等。总之,ts绝对代表了未来的大前端语言趋势,不学就落后了。
ts环境开发
一般情况下,可以在babel或者ts的在线练习环境来快速体验ts语言。但是,为了做项目,还是需要在本地安装好开发环境的,接下来,我们就看看如何在本地配置开发环境和IDE的。
安装compiler
npm i -g typescript
tsc --version
// 新建一个test.ts文件
export class testTS {
}
tsc test.ts --->编译--->test.js
"use strict";
exports.__esModule = true;
var testTS = /** @class */ (function () {
function testTS() {
}
return testTS;
}());
exports.testTS = testTS;
与vscode进行集成
tsc --init // 创建tsconfig.json
有了tsconfig.json文件后,就可以对文件进行配置,设置好js文件夹和ts文件夹。
编译ts文件
使用vscode编译ts文件非常的方便,只需要run task即可。
run task后会出现tsc:build 、tsc:watch两个选项,tsc:build是快速编译,tsc:watch是实时、监控编译,编译好后,文件就会存在于在tsconfig.json指定好的目录下了。
前端工程比较庞大的时候,或者需要将多个ts输出到多个js文件夹下的时候,建议使用webpack、gulp或者grunt来进行项目的打包和编译。这几个工具都是前端工程化的工具,在此不做一一展开来讲了。
简单学学ts语法
ts语法很多也是es6语法,因此,简单说说一些ts语法,全当对es6进行复习了。
字符串特性
多行字符串
使用``
字符串模板
使用`${xxx}`
自动拆分字符串
下图,test函数可以通过字符串模板来调用参数,就是后边的这些,可以用字符串模板调用,然后,自动拆分
参数
指定参数类型
使用【冒号 = :】来指定变量、参数、函数返回值类型,并且,类型具有推断机制,如果给string类型的变量,赋值数字的话,IDE会报错。(这里也仅仅是IDE会报错,因此,js是动态语言,不存在类型的概念,通过ts转换为js不管怎么说都是对的)
另外,通过类的概念自定类型后,自定义的类型也可以作为普通类型使用
var nymm:string='11111dssasd'
var nymm:any='11111dssasd'
var nymm:number='11111dssasd'
var nymm:boolean='11111dssasd'
// 可选参数,b就是可选参数,可以不传递
function test(er:string,ki:string = "ddd",op?:string):void{
}
// 自定义类型
class Car {
name:string;
logo:string
}
var ben:Car = new Car();
默认参数与可选参数
给参数赋默认值,如果有普通参数,那么,默认参数不可以为第一个参数,否则,在赋值的时候,会对参数的赋值进行覆盖。同时,还提供了【问号 = ?】来指定其为可选参数,并且,可选参数必须放在必选参数之后。
function test(er:string,ki:string = "ddd",op?:string):void{
}
函数
Rest and Spread操作符
Rest and Spread操作符,例如【...args】,这个操作符表示可以声明任意数量的方法参数。这个参数表示的是一个数组,调用该参数的时候,要按照数字的方式进行处理。
// 任意长度参数定义
function fun(...args){}
// 定长参数定义
function fun2(a,b,c)
var ays = [1,2,9]
// 定长函数调用
fun2(...ays)
generator和async函数操纵顺序程序
generator以及async函数,都是promise的语法糖。用来控制函数的执行过程,通过yield和await来控制代码的执行。配合着next(),来控制顺序程序的暂停和开启。然后,每次,调用next(),都会停在某一个yield或者async。
对于异步函数,其实就是promise的返回,因此,直接操作返回的对象即可。
析构表达式(destructuring)
通过表达式将对象或数组拆解成任意数量的变量
拆解对象
拆解数组
箭头表达式
用来声明匿名函数,消除传统匿名函数的this指针问题,因为,js中的this关键字,会发生js指向和预期指向不一致的情况。因为,this的指向是调用自己的函数的指向,但是,因为闭包、函数嵌套调用等原因,this的指向会发生改变。通过箭头表达式,将this指向了最外层的函数,从而,让一个函数内共享了this内存,从而解决指向改变的问题。
最简单的匿名函数
var kill = (bill,kate)=>bill + kate;
等价于
var kill = function (bill,kate) {return bill + kate};
例如,上图的情况,this指向了匿名函数,因此会打印不出值。
对于非箭头函数表达式下的this的简单说明
在普通函数中,this指代的是调用他的函数,例如
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
这个函数打印的就不是一个值,因为,foo是引用,因此是全局环境下的对象对于this的调用,因此,值是不一样的。
当然,关于this,很有必要单独开一个文章来讲解
循环
本节将要讲解forEach()、for in、for of循环的异同
forEach()
forEach(),有两个特性,第一是不能break跳出,第二是会忽略对象的展示。
for in
for in循环的是keys,同时可以使用break跳出。
for of
for of循环的是values,同时可以使用break跳出。
面向对象
ts的面向对象,或者说js的面向对象与c++或者java很不一样。因为这种解释型的语言,本身就已经是对象了,因此,在操纵内存的时候要格外小心。
Class
ts要求使用面向对象的特性来书写程序,既然是面向对象,那么,就可以更好的利用继承、封装和多态了。使用面向对象,可以更好的实现抽象定义和整个程序的架构。
定义
我们可以看到es5下,类的定义是基于匿名函数和闭包的,同时还提供了prototype这个属性。在此不做展开,大家自行查阅资料。(此处涉及到匿名函数的调用和原型链的知识,此处建议学习v8引擎的相关说明资料https://v8.dev/docs和ECMAScript的说明文档https://tc39.es/ecma262/#sec-ecmascript-specification-types)
另外,对于上边的代码,特别要注意的是,在es5下,匿名函数一定要自执行后,才能调用,因为,在没有执行前,匿名函数没有意义。
访问控制符
默认的访问控制符是public,同时还可以添加private和protected。public表示都可以访问,private表示内部访问,protected表示内部包或者子类可以访问。
构造函数(constructor())
构造函数的参数,一定要显式的做访问控制符的声明,否则需要在外部单独指定其访问控制权限。
继承(extends和super)
如果我们用es5来实现class和extends语法糖的话,那么就应该是这样的几个函数,此处又涉及到多个问题,因此,之后会专门开个文章系列来讨论这部分内容。另外,通过查看es5的实现方式,我们也明白了,extends和super的意义。
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Animal = /** @class */ (function () {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function (distanceInMeters) {
if (distanceInMeters === void 0) { distanceInMeters = 0; }
console.log(this.name + " moved " + distanceInMeters + "m.");
};
return Animal;
}());
var Snake = /** @class */ (function (_super) {
__extends(Snake, _super);
function Snake(name) {
return _super.call(this, name) || this;
}
Snake.prototype.move = function (distanceInMeters) {
if (distanceInMeters === void 0) { distanceInMeters = 5; }
console.log("Slithering...");
_super.prototype.move.call(this, distanceInMeters);
};
return Snake;
}(Animal));
var Horse = /** @class */ (function (_super) {
__extends(Horse, _super);
function Horse(name) {
return _super.call(this, name) || this;
}
Horse.prototype.move = function (distanceInMeters) {
if (distanceInMeters === void 0) { distanceInMeters = 45; }
console.log("Galloping...");
_super.prototype.move.call(this, distanceInMeters);
};
return Horse;
}(Animal));
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
泛型(generic)
参数化的类型,一般用来限制集合的内容。换句话说,就是声明某个集合必须是什么数据类型。
var workers:Array<Worker> = []
这个数组就要求,我们只能将worker放到数组中。
接口(interface)
用来建立某种代码约定,使得其他开发者在调用某个方法或者创建新的类时必须遵循接口所定义的代码约定。使用interface定义,使用implements引用。
定义接口并使用
基于implements实现接口
模块(Module)
在ts或者js中,一个文件,其实就是一个模块,通过export和import进行模块的导出和导入。
模块可以帮助开发者将代码分割为可重用的单元。开发者可以自己决定将模块中的哪些资源(类、方法、变量)暴露出去供外部使用,哪些资源只在模块内部使用。
值得注意的是,对于继承来说,因为,类肯定也是在不同模块中的,因此,我们既要将模块import,还要extends对应的类。(此处要注意CommonJS(CMD、AMD)和es6模块的异同)
注解(annotation)
注解为程序的元素(类、方法、变量)加上更加直观的说明,这些说明信息与程序的业务逻辑无关,只是供指定的工具或者框架使用。(关于注解,可以具体查看相关工具的文档)
类型定义文件(*.d.ts)
ts想要使用js工具包已有的程序应该怎么办呢?之前以及说过了,js可以不经修改的在ts下执行,那么,我们其实可以直接调用js,但是,又存在语法差异,不免会产生bug,因此,使用类型定义文件来帮助ts调用js的已有代码,放在bug的产生。例如:koa等
类型定义文件,来源于DefinitelyTyped项目https://github.com/DefinitelyTyped/DefinitelyTyped
例如koa的类型定义文件就是在这个下边https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/koa/index.d.ts,通过 npm i @types/koa便可以安装这个项目,也可以通过typings来安装和查找。这个项目中,有大量的已有js项目的类型定义文件。
注意,其实还可以通过通过typings(https://github.com/typings/typings)来寻找以及存在的类型定义文件。但是,由于官方推荐 NPM @types的形式来安装类型定义文件,因此,可以不去安装typings了,直接使用npm的安装方式即可。
npm WARN deprecated typings@2.1.1: Typings is deprecated in favor of NPM @types -- see README for more information
npm WARN deprecated popsicle-proxy-agent@3.0.0: Use `agent` option with `popsicle` directly
npm i typings -g
typings search --name project_name
typings install project_name --save
后记
本文概要的讲解了ts和ts的面向对象,对于很多知识都没有展开来讲,例如,es5为什么要那样构建类,class、extends等语法糖的es5版本为啥要那样写,注解如何使用等等。
对于此部分的内容,我将会在后续的专题中,深入讨论的。
讨论一下ts做后端开发
对于使用过Java、PHP、C#等开发后端的程序员来说,ts语法会让你们感到更加的亲切。但是,因为,ts本身还在发展,因此,例如装饰器、注解等还是需要第三方库进行支持。
Routing-controllers库
https://github.com/typestack/routing-controllers
该库为express、koa添加了装饰器模式,可以通过注解的方式来操控web服务器。
TypeDI依赖注入库
https://github.com/pleerock/typedi