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
裝飾器執行順序:
屬性裝飾器>方法裝飾器>方法參數裝飾器(從後到前)>類裝飾器(從後到前)