Linux awk命令常見使用方法介紹
初識TypeScript
TypeScript 是 JavaScript 的一個超集,主要提供了型別系統和對 ES6+ 的支援,它由 Microsoft 開發,程式碼開源於 GitHub上
安裝TypeScript
命令列執行如下命令,全域性安裝TypeScript:
npm install -g typescript
- 安裝完成後,在控制檯執行如下命令,檢查安裝是否成功:
tsc -V
第一個TypeScript程式
- 編寫TS程式
//helloworld.ts function greeter(person){ return 'hello,'+person } let user='tao' console.log(greeter(user))
手動編譯程式碼
- 我們使用了.ts副檔名,但是這段程式碼僅僅是JS而已,在命令列上,執行 TypeScript 編譯器:
tsc helloworld.ts
輸出結果為一個helloworld.js檔案,它包含了和輸入檔案中相同的JS程式碼,在命令列上,通過Node.js執行這段程式碼:node helloworld.js
控制檯輸出:
hello,tao
vscode自動編譯
-
生成配置檔案tsconfig.json
tsc --init
-
修改tsconfig.json配置
"outDir":"./js",
"strict":false,
3.啟動監視任務
- 終端->執行任務->監視tsconfig.json
- 型別註解
給函式引數新增:string型別的註解,如下:
function greeter(person:string){
return 'hello,'+person
}
let user='tao'
console.log(greeter(user))
- TypeScript 裡的型別註解是一種輕量級的為函式或變數新增約束的方式。在這個例子裡,我們希望函式接受一個字串引數。然後嘗試傳入陣列:
function greeter(person:string){ return 'hello,'+person } let user=[0,1,2] console.log(greeter(user))
重新編譯,產生了一個錯誤:
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
介面
- 使用介面來描述一個擁有firstName和lastName欄位的物件。在TypeScript裡,只在兩個型別內部的結構,那麼這兩個型別就是相容的。這就允許我們在實現介面的時候只要保證包含了介面要求的結構就可以,而不必明確地使用implement語句
interface Person{
firstName:string,
lastName:string
}
function greeter(person:Person){
return 'hello,'+person.firstName+person.lastName
}
let user={
firstName:'tao',
lastName:'xie'
}
console.log(greeter(user))
類
- 建立一個User類,它帶有一個建構函式和一些公共欄位。因為類的欄位包含了介面所需要的欄位,所以他們能很好的相容
class User {
fulName:string,
firstName:string,
lastName:string
constructor(firstName:string,lastName:string){
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName + ' ' + lastName
}
}
interface Person {
firstName: string
lastName: string
}
function greeter (person: Person) {
return 'Hello, ' + person.firstName + ' ' + person.lastName
}
let user = new User('tao', 'xie')
console.log(greeter(user))
TypeScript常用語法
- 基礎型別
- 布林值
最基本的資料型別就是簡單的true/false,在JS和TS裡叫做boolean
let isDone:boolean=false
isDone=true
//isDone=2 //error
- 數字
和JS一樣,TS裡的所有數字都是浮點數。這些浮點數的型別都是number。除了支援十進位制和十六進位制字面量,TS還支援ECMAScript2015中引入的二進位制和八進位制字面量
let a1:number=10 //十進位制
let a2:number=0b1010 //二進位制
let a3:number=0o12 //八進位制
let a4:number=0xa //十六進位制
- 字串
使用string表示文字資料型別。和JS一樣,可以使用雙引號"或者單引號'表示字串
let name:string='tom'
name='jack'
//name=12 //error
let age:number=12
const info=`My name is ${name},I am ${age} years old`
- undefined和null
預設情況下,null和undefined是所有型別的子型別。
let u:undefined=undefined
let u:null=null
- 陣列
//第一種定義陣列:直接在元素型別後面加[]
let list1:number[]=[1,2,3]
//第二種定義陣列
let list2:Array<number>=[1,2,3]
- 元組Tuple
元組型別允許表示一個已知元素數量和型別的陣列,各陣列元素不必相同。
let t1:[string,number]
t1=['hello',1] //ok
t1=[1,'hello'] //error
當訪問一個已知索引的元素,會得到正確的型別:
console.log(t1[0].substring(1)) //ok
console.log(t1[1].substring(1)) //error number不存在substring方法
- 列舉
enum使用列舉型別可以為一組陣列賦予友好的名字
enum Color {
Red,
Green,
Blue
}
//列舉數值預設從0開始依次遞增
//根據特定的名稱得到對應的列舉型別
let myColor:Color = Color.Green //0
console.log(myColor,Color.Red,Color.Blue)
預設情況下,從0開始為元素編號,也可以指定成員的數值:
enum Color {
Red=1,
Green,
Blue
}
let c:Color=Color.Green
或者全部採用手動賦值:
enum Color {
Red=1,
Green=2,
Blue=4
}
let c:Color=Color.Green
列舉型別提供一個便利是可以由列舉的值得到它的名字:
enum Color {
Red=1,
Green,
Blue
}
let colorName:string=Color[2]
console.log(colorName) //Green
- any
let notSure:any=4
notSure='maybe a string'
notSure=false
在對現有程式碼進行改寫的時候,any型別是十分有用的,它允許在編譯時可選擇地包含或移除檢查。並且當只知道一部分資料的型別時,any型別也是有用的:
let list:any[]=[1,true,'free']
list[1]=100
- void
某種程度上,void型別像是與any型別相反,它表示沒有任何型別。當一個函式沒有返回值時,通常會見到其返回值型別的是void:
/*表示沒有任何型別,一般用來說明函式的返回值不能是undefined和null之外的值*/
function fn():void{
console.log('fn()')
//return undefined
//return null
//return 1 //error
}
宣告一個void型別的變數沒有什麼用,因為你只能為它賦予undefined和null:
let unusable:void=undefined
- object
object表示非原始型別,也就是除number,string,boolean之外的型別
使用object型別,就可以更好的表示像Object.create這樣的API:
function fn2(obj:object){
console.log('fn2()',obj)
return {}
// return undefined
// return null
}
console.log(fn2(new String('abc')))
//console.log(fn2('abc')) //error
console.log(fn2(String))
- 聯合型別
聯合型別表示取值可以為多種型別中的一種
需求1:定義一個一個函式得到一個數字或字串值的字串形式值
function toString2(x:number|string):string{
return x.toString()
}
需求2:定義一個一個函式得到一個數字或字串值的長度
function getLength(x:number|string){
//return x.length //error
if(x.length){ //error
return x.length
}else{
return x.toString().length
}
}
型別斷言
通過型別斷言這種方式可以告訴編譯器,“相信我,我知道自己在幹什麼”。型別斷言好比其他語法裡的型別轉換,但是不進行特殊的資料檢查和解構。它沒有執行時的影響,只是在編譯階段起作用。
型別斷言有兩種形式。其一是 '尖括號’語法,另一個為as語法
/*
型別斷言:可以用來手動指定一個值的型別
語法:
方式一:<型別>值
方式二:值 as 型別 tsx中只能用這種方式
*/
/*需求:定義一個函式得到一個字串或者數值資料的長度*/
function getLength(x:number|string){
if((<string>x).length){
return (x as string).length
}else{
return x.toString().length
}
}
console.log(getLength('abcd'),getLength(1234))
型別推斷
型別推斷:TS在沒有明確的指定型別的時候推測出一個型別
有下面2種情況:1.定義變數時賦值,推斷為對應的型別 2.定義變數時沒有賦值,推斷為any型別
/*定義變數時賦值,推斷為對應的型別*/
let b9=123. //number
//b9='abc' //error
/*定義變數時沒有賦值,推斷為any型別*/
let b10 //any
b10=123
b10='abc'
介面
介面是物件的狀態(屬性)和行為(方法)的抽象(描述)
- 介面初探
需求:建立人的物件,需要對人的屬性進行一定的約束
id是number型別,必須有,只讀
name是string型別,必須有
age是number型別,必須有
sex是string型別,可以沒有
下面通過一個簡單示例來觀察介面是如何工作的:
/*
在ts中,我們使用介面(interface)來定義物件的型別
介面:是物件的狀態(屬性)和行為(方法)的抽象(描述)
介面型別的物件
多了或者少了屬性是不允許的
可選屬性:?
只讀屬性:readonly
*/
/*
需求:建立人的物件,需要對人的屬性進行一定的約束
id是number型別,必須有,只讀
name是string型別,必須有
age是number型別,必須有
sex是string型別,可以沒有
*/
//定義人的介面
interface IPerson{
id:number,
name:string,
age:number,
sex:string
}
const person1:Person={
id:1,
name:'tom',
age:20,
sex:'男'
}
型別檢查器會檢視物件內部的屬性是否與Iperson介面描述一致,如果不一致就會提示型別錯誤
- 可選屬性
接口裡的屬性不全是必需的。有些是隻在某些條件下存在,或者不存在
interface IPerson{
id:number,
name:string,
age:number,
sex?:string
}
帶有可選屬性的介面與普通介面定義差不多,只是在可選屬性名字定義的後面加一個?號
可選屬性的好處之一是可以對可能存在的屬性進行預定義,好處之二是可以捕獲引用了不存在的屬性時的錯誤
const person2:IPerson={
id:1,
name:'tom',
age:20,
//sex:'男' //可以沒有
}
- 只讀屬性
一些物件屬性只能在物件剛剛建立的時候修改其值。可以在屬性名前用readonly來指定只讀屬性:
interface IPerson{
readonly id:number,
name:string,
age:number,
sex?:string
}
一旦賦值後再也不能被改變
const person2:IPerson={
id:2,
name:'tom',
age:20,
//sex:'男' //可以沒有
//xxx:12. //error 沒有在介面中定義,不能有
}
person2.id=2 //error
readonly vs const
最簡單判斷該用readonly還是const的方法是看要把它作為變數使用還是作為一個屬性。作為變數使用的話用const,作為屬性使用的話用readonly
- 函式型別
介面能夠描述JS中物件擁有的各種各樣的外形。除了描述帶有屬性的普通物件外,介面也可以描述函式型別。
為了使用介面表示函式型別,需要給介面定義一個呼叫簽名。就像是一個只有引數列表和返回值型別的函式定義。引數列表裡的每個引數都需要名字和型別。
/*
介面可以描述函式型別(引數的型別與返回的型別)
*/
interface SearchFunc{
(source:string,subString:string):boolean
}
這樣定義後,可以像使用其他介面一樣使用這個函式型別的介面。下例展示瞭如何建立一個函式型別的變數,並將一個同類型的函式賦值給這個變數:
const mySearch:SearchFunc=function(source:string,sub:string):boolean{
return source.search(sub)>-1
}
console.log(mySearch('abcd','bc'))
類型別
- 類實現介面
/*
類型別:實現介面
1.一個類可以實現多個介面
2.一個介面可以繼承多個介面
*/
interface Alam{
alert():any
}
interface Light{
lightOn():void
lightOff():void
}
class Car implements Alam {
alert(){
console.log('Car alert')
}
}
一個類可以實現多個介面
class Car2 implements Alam,Light{
alert(){
console.log('Car alert')
},
lightOn(){
console.log('Car light on')
},
lightOff(){
console.log('Car light off')
}
}
- 介面繼承介面
和類一樣,介面也可以相互繼承。能夠從一個接口裡複製成員到另一個接口裡,可以更靈活地將介面分割到可重寫的模組裡
interface LightableAlarm extends Alam,Light{
}
類
基本示例:
/*
類的基本定義與使用
*/
class Greeter{
//宣告屬性
message:string
//構造方法
constructor(message:string){
this.message=message
}
//一般方法
greet():string{
return 'hello'+this.message
}
}
//建立類的例項
const greeter=new Greeter('world')
//呼叫例項的方法
console.log(greeter.greete())
在引用任何一個類成員的時候都用了this。它表示訪問的是類的成員。
後面一行,使用new構造了Greeter類的例項。它會呼叫之前定義的建構函式,建立一個Greeter型別的新物件,並執行建構函式初始化它。
最後一行通過greeter物件呼叫其greet方法
繼承
基於類的程式設計中一種最基本的模式是允許使用繼承來擴充套件現有的類。
/*
類的繼承
*/
class Animal{
run(distance:number){
console.log(`Animal run ${distance}m`)
}
}
class Dog extends Animal{
cry()
console.log('wang! wang!')
}
}
const dog=new Dog()
dog.cry()
dog.run(100) //可以呼叫從父中繼承得到的方法
這個例子展示了最基本的繼承:類從基類中繼承了屬性和方法。這裡,Dog是一個派生類,它派生自Animal基類,通過extends關鍵字。派生類通常被稱作子類,基類通常被稱作超類。
因為Dog繼承了Animal的功能,因此我們可以建立一個Dog的例項,它能夠cry()和run()
class Animal {
name:string
constructor (name;string){
this.name=name
}
run(distance:number=0){
console.log(`${this.name} run ${distance} m`)
}
}
class Snake extends Animal {
constructor(name:string){
//呼叫父型別構造方法
super(name)
}
//重寫父型別的方法
run(distance:number=5){
console.log('sliding...')
super.run(distance)
}
}
class Horse extends Animal {
constructor (name:string){
//呼叫父型別構造方法
super(name)
}
//重寫父型別的方法
run(distance:number=50){
console.log('dashing...')
//呼叫父型別的一般方法
super.run(distance)
}
xxx(){
console.log('xxx()')
}
}
const snake=new Snake('sn')
snake.run()
const horse=new Horse('ho')
horse.run()
//父型別引用指向子型別的例項=》多型
const tom:Animal=new Horse('ho22')
tom.run()
/*如果子型別沒有擴充套件的方法,可以讓子型別引用指向父型別的型別*/
const tom3:Snake=new Animal('tom3')
tom3.run()
/*如果子型別有擴充套件的方法,不能讓子型別引用指向父型別的例項*/
//const tom2:Horse=new Animal('tom2')
//tom3.run()
使用extends關鍵字建立了Animal的兩個子類:Horse和Snake。派生類包含一個建構函式,它必須呼叫super(),它會執行基類的建構函式。而且,在建構函式裡訪問this的屬性之前,一定要呼叫super()。這是TS強制執行的一條重要規則。
這個例子演示瞭如何在子類裡可以重寫父類的方法。Snake類和Horse類都建立了run方法,它們重寫了從Animal繼承來的run方法,使得run方法根據不同的類而具有不同的功能。注意,即使tom被宣告為Animal型別,但因為它的值是Horse,呼叫tom.run(34)時,它會呼叫Horse裡重寫的方法。
公共,私有與受保護的修飾符
-
預設為public
可以自由的訪問程式裡定義的成員。在TS裡,成員都預設為public。也可以明確的將一個成員標記為public。 -
理解private
當成員被標記為private時,它就不能在宣告它的類的外部訪問。 -
理解protected
protected修飾符與private修飾符的行為很相似,但有一點不同,protected成員在派生類中仍然可以訪問。
/*
訪問修飾符:用來描述類內部的屬性/方法的可訪問性
public:預設值,公開的外部可以訪問
private:只有類內部可以訪問
protected:類內部和子類可以訪問
*/
class Animal {
public name:string
public constructor(name:string){
this.name=name
}
public run(distance:name=0){
console.log(`${this.name} run ${distance}m`)
}
}
class Person extends Animal{
private age:number=18
protected sex:string='男'
run (distance:number){
console.log('Perosn jumping...')
super.run(distance)
}
}
class Student extends Person{
run(distance:number=6){
console.log('Studnet jumping...')
console.log(this.sex) //子類能看到父類中受保護的成員
//console.log(this.age) //子類看不到父類中私有的成員
super.run(distance)
}
}
console.log(new Person('abc').name) //公開的可見
//console.log(new Person('abc').sex) //受保護的不可見
//console.log(new Person('abc').age). //私有的不可見
readonly修飾符
可以使用readonly關鍵字將屬性設定為只讀的。只讀屬性必須在宣告時或建構函式裡被初始化。
class Person{
readonly name:string='abc'
constructor(name:string){
this.name=name
}
}
let john=new Person('John')
//john.name='peter' //error
引數屬性
在上面的例子中,必須在Person類裡定義一個只讀成員name和一個引數為name的建構函式,並且立刻將name的值賦給this.name,這種情況經常會遇到。引數屬性可以方便地讓在一個地方定義並初始化一個成員。
class Person2{
constructor (readonly name:string){
}
}
const p=new Person2('jack')
console.log(p.name)
注意看是如何捨棄引數name,僅在建構函式裡使用readonly name:string引數來建立和初始化name成員。把宣告和賦值合併至一處。
引數屬性通過給建構函式引數面前新增一個訪問限定符來宣告。使用private限定一個引數屬性會宣告並初始化一個私有成員;對於public和protected來說也是一樣。
存取器
Typescript支援通過getters/setters來擷取對物件成員的訪問。它能幫助你有效的控制對物件成員的訪問。
class Person {
firstName:string='A'
lastName:string='B'
get fullName(value){
return this.firstName+'-'+this.lastName
}
set fullName(value){
const names=value.split('-')
this.firstName=name[0]
this.lastName=name[1]
}
}
const p=new Person()
console.log(p.fullName)
p.firstName='C'
p.lastName='D'
console.log(p.fullName)
p.fullName='E-F'
console.log(p.firstName,p.lastName)
靜態屬性
可以建立類的靜態成員,這些屬性存在於類本身而不是類的例項上。
/*
靜態屬性:是類物件的屬性
非靜態屬性:是類的例項物件的屬性
*/
class Person{
name1:string='A'
static name2:string='B'
}
console.log(Person.name2)
console.log(new Person().name1)
抽象類
抽象類作為其他派生類的基類使用。它們不能被例項化。不同於介面,抽象類可以包含成員的實現細節。abstract關鍵字是用於定義抽象類和在抽象類內部定義抽象的方法。
/*
抽象類
不能建立例項物件,只有實現類才能建立例項
可以包含未實現的抽象方法
*/
abstract class Animal{
abstract cry()
run(){
console.log('run()')
}
}
class Dog extends Animal{
cry(){
console.log('Dog cry()')
}
}
const dog=new Dog()
dog.cry()
dog.run()
函式
- 基本例項
和JS一樣,TS函式可以建立有名字的函式和匿名函式。你可以隨意選擇合適應用程式的方式,不論是定義一系列API函式還是隻使用一次的函式。
下面的例子可以迅速回想起JS的兩種函式:
//命名函式
function add(x,y){
return x+y
}
//匿名函式
let myAdd=function(x,y){
return x+y
}
函式型別
- 為函式定義型別
為上面函式新增型別
function add(x:number,y:number):number{
return x+y
}
let myAdd=function(x:number,y:number):number{
return x+y
}
可以給每個引數新增型別之後再為函式本身新增返回值型別。TS能夠根據返回語句自動推斷出返回值型別。
書寫完整函式型別let myAdd2:(x:number,y:number)=>number=function(x:number,y:number):number{ return x+ y }
可選引數和預設引數
TS裡的每個函式引數都是必須的。這不是指不能傳遞null和undefined作為引數,而是說編譯器檢查使用者是否為每個引數都傳入了值。編譯器還會假設只有這些引數會被傳遞進函式。簡短地說,傳遞給一個函式的引數個數必須與函式期望的引數個數一致。
JS裡,每個引數都是可選的,可傳可不傳。沒有傳參的時候,它的值就是undefined。在TS裡可以在引數名旁使用?實現可選引數的功能。
function bulidName(firstName:string='A',lastName?:string):string {
if(lastName){
return firstName+'-'+lastName
} else {
return firstName
}
}
console.log(bulidName('C','D'))
console.log(bulidName('C'))
console.log(bulidName())
剩餘引數
必要引數,預設引數和可選引數有個共同點:它們表示某一個引數。有時,想同時操作多個引數,或者並不知道會有多少引數傳遞進來。在JS裡,可以使用arguments來訪問所有傳入的引數。
在TS裡,可以把所有引數收集到一個變數裡:剩餘引數會被當做個數不限的可選引數。可以一個都沒有,同樣也可以有任意個。編譯器建立引數陣列,名字是你在省略號(...)後面給定的名字,可以在函式體內使用這個陣列。
function info(x:string,...args:string[]){
console.log(x,args)
}
info('abc','c','b','a')
函式過載
函式過載:函式名相同,而形參不同的多個函式
/*
函式過載:函式名相同,而形參不同的多個函式
需求:我們有一個add函式,它可以接收2個string型別的引數進行拼接,也可以接收2個number型別的引數進行想加
*/
//過載函式宣告
function add(x:string,y:string):string
function add(x:number,y:number):number
//定義函式實現
function add(x:string | number,y:string | number):string | number {
//在實現上要注意嚴格判斷兩個引數的型別是否相等,而不是簡單的寫一個x+y
if(typeof x==='string'&&typeof y==='string'){
return x+y
}else if(typeof x==='number'&&typeof y==='number'){
return x+y
}
}
console.log(add(1,2))
console.log(add('a'+'b'))
//console.log(1,'a') //error
泛型
指在定義函式、介面或類的時候,不預先指定具體的型別,而在使用的時候再指定具體型別的一種特性。
- 引入
下面建立一個函式,實現功能:根據指定的數量count和資料value,建立一個包含count個value的陣列不用泛型的話,這個函式可能是下面這樣:
function createArray(value:any,count:number):any[]{
const arr:any[]=[]
for(let index=0;index<count;index++){
arr.push(value)
}
return arr
}
const arr1=createArray(11,3)
const arr2=createArray('aa',3)
console.log(arr1[0].toFixed(),arr2[0].split(''))
使用函式泛型
function createArray2<T>(value:T,count:number){
const arr:Array<T>=[]
for(let index=0;index<count;index++){
arr.push(value)
}
return arr
}
const arr3=createArray2<number>(11,3)
console.log(arr3[0].toFixed())
// console.log(arr3[0].split('')) //error
const arr4=createArray2<string>('aa',3)
console.log(arr4[0].split(''))
//console.log(arr[4].toFixed()) //error
多個泛型引數的函式
//一個函式可以定義多個泛型引數
function swap<K,V>(a:K,b:V):[K,V]{
return [a,b]
}
const result=swap<string,number>('abc',123)
console.log(result[0].length,result[1].toFixed())
泛型介面
在定義介面時,為介面中的屬性或方法定義泛型型別
在使用介面時,再指定具體的泛型型別
interface ibaseCRUD<T>{
data:T[]
add:(t:T)=>void
getById:(id:number)=>T
}
class User{
id?:number;//id逐漸自增
name:string;//姓名
age:number;//年齡
constructor(name,age){
this.name=name
this.age=age
}
}
class UserCRUD implements IbaseCRUD <User>{
data:User[]=[]
add(user:User):void{
user={...user,id:Date.now()}
this.data.push(user)
console.log('儲存user',user.id)
}
getById(id:number):User{
return this.data.find(item=>item.id===id)
}
}
const userCRUD=new UserCRUD()
UserCRUD.add(new User('tom',12))
UserCRUD.add(new User('tom2',13))
console.log(userCRUD.data)
泛型類
在定義類時,為類中的屬性或方法定義泛型型別 在建立類的例項時,再指定特定的泛型型別
class GenericNubmer<T>{
zeroValue:T
add:(x:T,y:T)=>T
}
let myGenericNumber=new GenericNubmer<number>()
myGenericNumber.zeroValue=0
myGenericNumber.add=function(x,y){
return x+y
}
let myGenericString=new GenericNubmer<string>()
myGenericString.zeroValue='abc'
myGenericString.add=function(x,y){
return x+y
}
console.log(myGenericString.add(myGenericNumber.zeroValue,'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue,12))
泛型約束
直接對一個泛型引數取length屬性會報錯,因為這個泛型根本不知道它是否有這個屬性
//沒有泛型約束
function fn<T>(x:T):void{
//console.log(x.length) //error
}
可以使用泛型約束來實現
interface Lengthwise{
length:number
}
//指定泛型約束
function fn2<T extends Lengthwise>(x:T):void{
console.log(x.length)
}
需要傳入符合約束型別的值,必須包含length屬性:
fn2('abc')
//fn2(123) //error number沒有length屬性
其他
宣告檔案
當使用第三方庫時,需要引用它的生命檔案,才能獲取對應的程式碼補全、介面提示等功能
什麼是生命語句
假如想使用第三方庫jQuery,一種常見的方式是在html中通過