慕課網@JoJozhai 老師 TypeScript入門課程分享
<a >TypeScript入門</a>
ES5,ES6,JS,TypeScript間的關系
ES5、ES6是兩個版本的語言規則,他們規定了腳本語言如何去寫,瀏覽器對應的去解析而JS腳本語言應用的就是ES5規則,TypeScript(以下簡稱ts)應用的是ES6規則,且由微軟公司開發
?在我個人理解,TypeScript是用更豐富的語法簡便或優化了JavaScript里一些比較復雜、非人性化的代碼編寫過程,就如less、sass之于css
?值得一提的是:新版本的angular2是由谷歌團隊開發,基礎代碼應用TypeScript編輯的前端框架,因此可認為是由兩大科技巨頭主導的技術
TypeScript環境搭建
-
為何要搭建環境
目前的主流瀏覽器對TypeScript的支持還不夠完善,因此需要編譯工具將TypeScript代碼轉譯成主流的JS代碼才能得以使用
-
本地安裝TypeScript環境compiler
在NodeJs條件下命令行輸入以下代碼全局安裝TypeScript環境
npm install -g typescript
?安裝完成后輸入
tsc -v
?查看TypeScriptCompiler版本
-
tsc使用
進入到TypeScript文件所在的目錄,在此目錄的命令行中運行tsc命令將.ts文件轉譯為.js文件
tsc index.ts
(轉譯index.ts文件)
?會在相同目錄下生成一個index.js文件
-
IDE自動編譯
上面的方法在實際開發過程中顯得十分的笨拙,而主流的IDE都為ts文件準備了一套自動tsc的方法,例如在webstorm中,開啟設置項languages中的TypeScript勾選Enable TypeScript compiler選項,即可自動在ts文件下生成對應的js文件
-
webstorm配置界面
TypeScript語法
-
字符串
-
聲明多行字符串
?ts中允許聲明字符串中換行,如下
var str = '123
456
789';
轉譯后在js文件中代碼會自動添加換行符,如下
var str = '123\n456\n789';
-
字符串模板
?ts中可以用`符號(大鍵盤1左邊)對字符串模板進行引用,如下
var myName = 'programmer';
var getName = function () {return 'programmer'};
console.log(`I am ${myName}`); //此處小括號中不是引號
console.log(`I am ${getName()}`);
轉譯js代碼如下
var myName = 'programmer';
var getName = function () {
return 'programmer';
};
console.log("I am " + myName);
console.log("I am " + getName());
-
自動拆分字符串
當將字符串模板應用到某一函數中時,將根據某種規則將模板進行拆分,拆分成函數相應個數的參數并調用,例如
function test(template,name,age) {
console.log(template);
console.log(name);
console.log(age)
}
var myname = 'programmer';
var getAge = function () {
return 18;
}
test`hello my name is ${myname}, I am ${getAge() }`
在其轉譯并運行后,我們會發現模板被拆分為三個參數打印出來
對應temlpate參數的是一個數組
Array[3]
0:"hello my name is "
1:", I am "
2:""
對應name參數的是"programmer",對應age參數的是 18
也就相當于模板被兩段${}分割的每個部分構成了第一個參數,而兩個${}是另外兩個參數
-
參數、變量新特性
-
變量類型聲明
ts中聲明變量時可用冒號:
對其類型進行聲明,或ts文件會對其自動聲明。聲明過類型的變量用其他類型的值進行賦值時會引發ts報錯(注意:在被轉譯為js后并不會引發任何報錯),如下圖(波浪線即為報錯),而:any
則代表聲明為任意類型,可像js中隨意賦值
類型聲明 -
函數參數類型定義
同樣對函數參數的聲明此方法也成立,另外可以在函數聲明時對其是否返回值進行聲明,:void
表示無返回值
參數類型 聲明類屬性類型
在聲明類和對已聲明類進行調用時也可使用此規則,如
class Programmer {
skill: string;
age: number;
}
var me: Programmer = new Programmer();
me.age = 18;
me.skill = 'ts';
-
聲明函數參數默認值
在聲明函數時,可以用=
對參數聲明默認值,這樣在調用時若缺失此參數也不會報錯,而是會按默認值進行調用(注意:帶有默認值的參數一定要放在后面聲明,否則將報錯)
function test(a:string,b:string,c:string = 'ccc'){
console.log(a);
console.log(b);
console.log(c);
}
test('aaa','bbb');
//打印aaa,bbb,ccc
-
聲明可選參數
在聲明函數時,可以在參數后用?
表示此參數可選(注意:可選參數一定要放在必選參數之后,否則將報錯)
function test(a:string,b?:string,c:string = 'ccc'){
console.log(a);
console.log(b);
console.log(c);
}
test('aaa');
//打印aaa,undefined,ccc
-
函數新特性
-
rest and spread操作符
ts可以在聲明函數時用...args
聲明不定數量個參數
function test(...args) {
args.forEach(function (arg) {
console.log(arg)
})
};
test(1, 2, 3, 4);
//打印1,2,3,4
轉譯為js后如下
function test() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
args.forEach(function (arg) {
console.log(arg);
});
}
;
test(1, 2, 3, 4);
也可以對聲明了固定數量參數的函數進行參數數量不等的調用
function test(a,b,c) {
console.log(a);
console.log(b);
console.log(c);
};
var arr = [1, 2];
test(...arr);//打印1,2,undefined
var arr2 = [1, 2, 3, 4, 5, 6];
test(...arr2);//只識別前三個參數,打印1,2,3
-
generator函數
通過function*
聲明一個generator函數在函數中添加yield
和next()
可對其進行打斷和分布執行操作,如下
function* test(){
console.log("start");
yield;
console.log("finish");
}
var test1 = test();
//須將函數聲明為變量使用此方法
test1.next();
test1.next();
每個.next()
執行yield
分割的一段代碼,第一個test1.next();
執行至yield
之前停止打印出"start",第二個test1.next()
執行之后的代碼,打印出"finish"
-
析構表達式
- 針對對象
在ts中若想一對一地將含有多個屬性的對象的函數返回值賦予多個變量時,可使用析構表達式,代碼如下
- 針對對象
function programmer(){
return {
name:"Tom",
skill:{
skill1:"TypeScript",
skill2:"AngularJs"
},
age:18
}
}
var {name,age} = programmer();
//此簡寫方法須滿足變量名與函數中的屬性名對應相同
console.log(name,age);
//打印出Tom 18
var {name:name1,skill:{skill1:skills}} = programmer();
//此為變量名與函數中屬性名不相同的寫法
console.log(name1,skills);
//打印出Tom TypeScript
轉譯后的js代碼如下
"use strict";
function programmer() {
return {
name: "Tom",
skill: {
skill1: "TypeScript",
skill2: "AngularJs"
},
age: 18
};
}
var _programmer = programmer();
var name = _programmer.name;
var age = _programmer.age;
console.log(name, age);
var _programmer2 = programmer();
var name1 = _programmer2.name;
var skills = _programmer2.skill.skill1;
console.log(name1, skills);
另外還可以在其中使用之前提過的Rest and Spread操作符拆分對象
var obj = {a:1,b:2,c:3,d:4};
var {a,b,...others}=obj;
//打印出1 2 {"c":3,"d":4} *將后兩個屬性打包進others中
轉譯后js代碼
"use strict";
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var obj = { a: 1, b: 2, c: 3, d: 4 };
var a = obj.a;
var b = obj.b;
var others = _objectWithoutProperties(obj, ["a", "b"]);
console.log(a, b, others);
- 針對數組
在數組中取值上面的方法一樣成立
var arr = [1,2,3,4];
var [num1,num2]=arr;
//注意此處是中括號
console.log(num1,num2);
//打印1 2
var [,,num3,num4]=arr;
console.log(num3,num4);
//打印3 4 用逗號空出對應位置即可跳躍取值
var [num5,,num6]=arr;
console.log(num5,num6);
//打印1 3
var [num7,...others]=arr;
console.log(num7,others);
//打印1 [2,3,4] *此時將剩余的三個數打包放進others中
轉譯后的js代碼如下
"use strict";
var arr = [1, 2, 3, 4];
var num1 = arr[0];
var num2 = arr[1];
console.log(num1, num2);
var num3 = arr[2];
var num4 = arr[3];
console.log(num3, num4);
var num5 = arr[0];
var num6 = arr[2];
console.log(num5, num6);
var num7 = arr[0];
var others = arr.slice(1);
console.log(num7, others);
-
函數表達式與循環
-
箭頭表達式
在ts中我們可以運用箭頭表達式=>
來聲明一個匿名函數
var test = arg1=>arg1;
//單個參數可以不加小括號
var sum = (arg1,arg2) =>arg1+arg2;
//單行聲明可以不加大括號
var sum2 = (arg1,arg2)=>{
return arg1+arg2
}
//多行寫法
轉譯后js代碼
"use strict";
var test = function test(arg1) {
return arg1;
};
var sum = function sum(arg1, arg2) {
return arg1 + arg2;
};
var sum2 = function sum2(arg1, arg2) {
return arg1 + arg2;
};
這樣看其實箭頭表達式只是比原來的聲明方法簡便些而已,其實不然。箭頭表達式真正強大的是可以很輕松的化解之前一直困擾我們得this的作用域問題
下面一段代碼在js中是打印不出東西的
function Person(x){
this.name=x;
setInterval(function(){
console.log(this.name)
},1000)
}
var man = new Person('zhangsan');
//setInterval中的this指向的是全局對象window而不是我們所創建的對象
而應用箭頭表達式就可以輕松解決作用域的問題
function Person(x){
this.name=x;
setInterval(()=>{
console.log(this.name)
},1000)
}
var man = new Person('zhangsan');
//每一秒打印出一個zhangsan
轉譯后js代碼中可以看出實際上是ts幫我們寫了糾正this這段代碼
function Person(x) {
var _this = this;
this.name = x;
setInterval(function () {
console.log(_this.name);
}, 1000);
}
var man = new Person('zhangsan');
- 新循環for of(與forEach、for in對比)
var arr = [1, 2, 3, 4];
arr.attr = 'number 5';
//這里在ts會報錯 但其實可以運行
arr.forEach((value,key) => console.log(value,key));
//打印出1,0|2,1|3,2|4,3
for (var i in arr) {
console.log(i);
};
//打印出1,2,3,4,attr
for (var n of arr) {
console.log(n);
}
//打印出1,2,3,4
for (var n of arr) {
if(n>2)break
console.log(n);
}
//打印出1,2
for of與另兩者最大的區別就在于for of會忽視掉循環中的非數字元素(for in不會),且可以打斷(forEach不可打斷)
-
面向對象特性
-
類的聲明
在ts中有一種專門的用class
引導的類聲明,例如下面的Programmer類聲明
class Programmer{
name;
skill;
work(){
console.log("coding....")
}
};
var pro1 = new Programmer();
pro1.name = 'zhang san';
pro1.skill = 'TS';
pro1.work();
//轉譯后js代碼
var Programmer = (function () {
function Programmer() {
}
Programmer.prototype.work = function () {
console.log("coding....");
};
;
return Programmer;
}());
var pro1 = new Programmer();
pro1.name = 'zhang san';
pro1.skill = 'TS';
pro1.work();
以上的代碼中,聲明了一個Programmer類,其中含有name,skill兩個屬性和work方法,而其調用方式與js中一樣。我們可以看到轉譯后,work()實際上是定義在原型上的方法。
* 類的訪問權限
在類的聲明同時,可以用public
private
protected
對其中屬性和方法的訪問權限進行聲明
圖中分別用三個訪問操作符聲明了3個屬性和一個方法,聲明private和protected的屬性和方法在外部,也就是class代碼的外面調用是會報錯的,而在內部work()方法可以任意調用屬性。
總結來說
public
操作符聲明的可以在任意地方使用,也是不聲明時的默認操作符;private
操作符會使其只可在class內部被調用;而protected
操作符則在class內部和該類的后代繼承元素上可以使用
public | protected | private | |
---|---|---|---|
外部 | √ | × | × |
后代 | √ | √ | × |
內部 | √ | √ | √ |
需要注意的是:這段代碼只是在書寫階段會引發報錯,而使用時不會有任何問題,因為轉譯成js后并不存在這些功能
-
類構造器
聲明類時可用constructor
關鍵字在類內部聲明一個函數,成為構造器函數,此函數只可在函數內部應用,當我們對類實例化的時候該函數就會運行一次
-
類構造器
class Programmer {
constructor() {
console.log('coding');
}
//構造器函數
};
var pro1 = new Programmer();
//打印一次coding
構造器函數的一個重要用途就是規定一個類里的某些屬性必須在實例化時被傳入值,如下
class Programmer {
name;
constructor(name:string) {
console.log('coding by '+name);
}
};
//也可以像下面這么寫
class Programmer {
constructor(public name:string) {
console.log('coding by '+name);
}
};
var pro1 = new Programmer();
//這行代碼會引發編輯器報錯
var pro2 = new Programmer('zhangsan');
//打印 coding by zhangsan
-
類的繼承
ts中也有繼承類的關鍵詞extends
,同js中一樣,子類通過extends
會繼承父類的所有方法和屬性,并可定義只屬于自身的方法和屬性
class Programmer{
name;
skill;
work(){
console.log("coding with "+this.skill)
}
};
class WebProgrammer extends Programmer{
no;
learn() {
console.log(this.no+' is learning TS')
}
}
var Wp1 = new WebProgrammer();
Wp1.skill = 'js';
Wp1.no = 1;
Wp1.work();
//打印 coding with js
Wp1.learn();
//打印 1 is learning Ts
-
構造函數繼承
ts語法規定在子類里聲明構造函數時,必須要通過super
關鍵詞在其構造函數內部調用父類的構造函數
class Programmer{
name;
skill;
constructor(name) {};
work(){
console.log("coding with "+this.skill)
}
};
class WebProgrammer extends Programmer{
no;
constructor(name:string,no:number) {
super(name);
this.no = no;
//在這里用super繼承了父類構造函數的name屬性,將name和no作為子類的兩個實例化時必須賦值的屬性
}
learn() {
console.log(this.no+' is learning TS')
}
}
var Wp1 = new WebProgrammer('zhangsan',1);
Wp1.skill = 'js';
Wp1.work();
Wp1.learn();
上面介紹的是用super
繼承父類的屬性,同樣父類的方法也可以繼承,下面是一個實例
class Programmer{
name;
skill;
constructor(name) { };
work(){
console.log("coding with "+this.skill)
}
};
class WebProgrammer extends Programmer{
no;
constructor(name:string,no:number) {
super(name);
this.no = no;
}
learn() {
super.work();
//繼承父類的work方法
this.learnAfterWork;
};
private learnAfterWork() {
//將該方法聲明為私有
console.log('learning sth')
};
}
var Wp1 = new WebProgrammer('zhangsan',1);
Wp1.skill = 'js';
Wp1.work();
//打印coding with js
Wp1.learn();
//打印coding with js,learning sth
Wp1.learnAfterWork();
//無法打印 因為方法learnAfterWork是私有的,外部訪問到
-
泛型
在ts中,可以用尖括號<>
對集合中元素的類型加以限制,概括的云山霧繞,一看代碼便知
class Programmer{
name;
skill;
work(){
console.log("coding with "+this.skill)
}
};
class WebProgrammer extends Programmer{
no;
learn() {
console.log(this.no+' is learning TS')
}
};
var Wp: Array<Programmer> = [];
//聲明Wp數組里只能放入Programmer類的元素
Wp[0] = new Programmer();
//成功
Wp[1] = new WebProgrammer();
//成功 WebProgrammer也屬于Programmer
Wp[2] = 1;
//在ts中會報錯
-
接口
ts中獨有的接口interface
用來形成一種約束,是的開發者在創建應用此接口的類或方法時必須要遵守接口中的代碼規則,其有兩種典型的使用方式,并且語法與聲明類十分相似- 1:對類的屬性進行約束
interface IProgrammer{
name: string;
age: number;
};
class Programmer{
constructor(public config: IProgrammer) {}
//構造函數中限制屬性要應用IProgrammer接口約束
};
var p1 = new Programmer();
//報錯
var p2 = new Programmer('zhangsan',18);
//報錯
var p3 = new Programmer({
name: 'zhangsan',
age:18
});
//正確調用方法:傳入一個帶有規定屬性的對象
- 2:對方法進行約束
對方法進行約束需要用到implements
關鍵詞,它規定被約束的方法內必須實現接口中的函數
interface IProgrammer{
useTool();
};
class JsProgrammer implements IProgrammer{
useTool(){
console.log('use JS')
}
};
class TsProgrammer implements IProgrammer{
useTool(){
console.log('use TS')
}
}
例子中若在聲明的方法中沒有實現useTool方法則會引發報錯
-
模塊
在ts中,每個ts文件就相當于一個模塊而在文件內部用export
,import
兩個關鍵字進行導出、導入模塊。只有在其他模塊已經導出的元素才可以在其他模塊中導入
例:在同一個目錄下建立兩個ts文件——export.ts和import.ts
export.ts代碼
export class klass1 { };
class klass2 { };
export var x1;
var x2;
export function func1() { };
function func(){};
//沒有加export的就是沒有輸出
import.ts代碼
import {klass1,x1,func1} from "export.ts";
x1=1;
var k1=new klass1;
func1();
//這里是取不到export.ts中沒有導出的klass2,x2,func2的
- 注解
ts注解是提供給指定的工具和框架使用的,為程序元素(類,方法,變量)加上更直觀的的說明,而與程序業務邏輯無關
例如angular2里的@component
(待研究.....)
- 類型定義文件
"那么在ts文件怎么應用js中的那些庫和框架呢?"
這時候就要用到類型定義文件xxx.d.ts
,如將jquery的類型定義文件index.d.ts放到ts同目錄下,這時jquery的接口就已經全部導出了,可以直接在ts文件中調用
而尋找各種各樣工具的類型定義文件就要用到