主頁 > 企業開發 > 最全TypeScript 入門基礎教程,看完就會

最全TypeScript 入門基礎教程,看完就會

2020-09-15 20:33:02 企業開發

想學習 TypeScript 的小伙伴看過來,本文將帶你一步步學習 TypeScript 入門相關的十四個知識點,詳細的內容大綱請看下圖:

 

img

 

一、TypeScript 是什么

 

TypeScript 是一種由微軟開發的自由和開源的編程語言,它是 JavaScript 的一個超集,而且本質上向這個語言添加了可選的靜態型別和基于類的面向物件編程,

 

TypeScript 提供最新的和不斷發展的 JavaScript 特性,包括那些來自 2015 年的 ECMAScript 和未來的提案中的特性,比如異步功能和 Decorators,以幫助建立健壯的組件,下圖顯示了 TypeScript 與 ES5、ES2015 和 ES2016 之間的關系:

 

img

 

1.1 TypeScript 與 JavaScript 的區別

 

img

 

1.2 獲取 TypeScript

 

命令列的 TypeScript 編譯器可以使用 Node.js 包來安裝,

 

1.安裝 TypeScript

 

 $ npm install -g typescript

 

2.編譯 TypeScript 檔案

 

 $ tsc helloworld.ts# helloworld.ts => helloworld.js

 

當然,對于剛入門 TypeScript 的小伙伴,也可以不用安裝 typescript,而是直接使用線上的 TypeScript Playground 來學習新的語法或新特性,

 

二、TypeScript 基礎型別

 

2.1 Boolean 型別

 

 let isDone: boolean = false;// ES5:var isDone = false;

 

2.2 Number 型別

 

 let count: number = 10;// ES5:var count = 10;

 

String 型別

 

 let name: string = "Semliker";// ES5:var name = 'Semlinker';

 

2.4 Array 型別

 

 let list: number[] = [1, 2, 3];// ES5:var list = [1,2,3];let list: Array<number> = [1, 2, 3]; // Array<number>泛型語法// ES5:var list = [1,2,3];

 

2.5 Enum 型別

 

使用列舉我們可以定義一些帶名字的常量, 使用列舉可以清晰地表達意圖或創建一組有區別的用例, TypeScript 支持數字的和基于字串的列舉,

 

1.數字列舉

 

 enum Direction {  NORTH,  SOUTH,  EAST,  WEST,}let dir: Direction = Direction.NORTH;

 

默認情況下,NORTH 的初始值為 0,其余的成員會從 1 開始自動增長,換句話說,Direction.SOUTH 的值為 1,Direction.EAST 的值為 2,Direction.WEST 的值為 3,上面的列舉示例代碼經過編譯后會生成以下代碼:

 

 ?
 enum Direction {
   NORTH,
   SOUTH,
   EAST,
   WEST,
 }
 ?
 let dir: Direction = Direction.NORTH;

 

當然我們也可以設定 NORTH 的初始值,比如:

 

 enum Direction {  NORTH = 3,  SOUTH,  EAST,  WEST,}

 

2.字串列舉

 

在 TypeScript 2.4 版本,允許我們使用字串列舉,在一個字串列舉里,每個成員都必須用字串字面量,或另外一個字串列舉成員進行初始化,

 

 enum Direction {  NORTH = "NORTH",  SOUTH = "SOUTH",  EAST = "EAST",  WEST = "WEST",}

 

以上代碼對于的 ES5 代碼如下:

 

 ?
 "use strict";
 var Direction;
 (function (Direction) {
     Direction["NORTH"] = "NORTH";
     Direction["SOUTH"] = "SOUTH";
     Direction["EAST"] = "EAST";
     Direction["WEST"] = "WEST";
 })(Direction || (Direction = {}));

 

3.異構列舉

 

異構列舉的成員值是數字和字串的混合:

 

 enum Enum {  A,  B,  C = "C",  D = "D",  E = 8,  F,}

 

以上代碼對于的 ES5 代碼如下:

 

 ?
 enum Enum {
   A,
   B,
   C = "C",
   D = "D",
   E = 8,
   F,
 }

 

通過觀察上述生成的 ES5 代碼,我們可以發現數字列舉相對字串列舉多了 “反向映射”:

 

 console.log(Enum.A) //輸出:0console.log(Enum[0]) // 輸出:A

 

2.6 Any 型別

 

在 TypeScript 中,任何型別都可以被歸為 any 型別,這讓 any 型別成為了型別系統的頂級型別(也被稱作全域超級型別),

 

 let notSure: any = 666;notSure = "Semlinker";notSure = false;

 

any 型別本質上是型別系統的一個逃逸艙,作為開發者,這給了我們很大的自由:TypeScript 允許我們對 any 型別的值執行任何操作,而無需事先執行任何形式的檢查,比如:

 

 let value: any;value.foo.bar; // OKvalue.trim(); // OKvalue(); // OKnew value(); // OKvalue[0][1]; // OK

 

在許多場景下,這太寬松了,使用 any 型別,可以很容易地撰寫型別正確但在運行時有問題的代碼,如果我們使用 any 型別,就無法使用 TypeScript 提供的大量的保護機制,為了解決 any 帶來的問題,TypeScript 3.0 引入了 unknown 型別,

 

2.7 Unknown 型別

 

就像所有型別都可以賦值給 any,所有型別也都可以賦值給 unknown,這使得 unknown 成為 TypeScript 型別系統的另一種頂級型別(另一種是 any),下面我們來看一下 unknown 型別的使用示例:

 

 ?
 let value: unknown;
 ?
 value = true; // OK
 value = 42; // OK
 value = "Hello World"; // OK
 value = []; // OK
 value = {}; // OK
 value = Math.random; // OK
 value = null; // OK
 value = undefined; // OK
 value = new TypeError(); // OK
 value = Symbol("type"); // OK

 

value 變數的所有賦值都被認為是型別正確的,但是,當我們嘗試將型別為 unknown 的值賦值給其他型別的變數時會發生什么?

 

 let value: unknown;let value1: unknown = value; // OKlet value2: any = value; // OKlet value3: boolean = value; // Errorlet value4: number = value; // Errorlet value5: string = value; // Errorlet value6: object = value; // Errorlet value7: any[] = value; // Errorlet value8: Function = value; // Error

 

unknown 型別只能被賦值給 any 型別和 unknown 型別本身,直觀地說,這是有道理的:只有能夠保存任意型別值的容器才能保存 unknown 型別的值,畢竟我們不知道變數 value 中存盤了什么型別的值,

 

現在讓我們看看當我們嘗試對型別為 unknown 的值執行操作時會發生什么,以下是我們在之前 any 章節看過的相同操作:

 

 ?
 let value: unknown;
 ?
 let value1: unknown = value; // OK
 let value2: any = value; // OK
 let value3: boolean = value; // Error
 let value4: number = value; // Error
 let value5: string = value; // Error
 let value6: object = value; // Error
 let value7: any[] = value; // Error
 let value8: Function = value; // Error

 

value 變數型別設定為 unknown 后,這些操作都不再被認為是型別正確的,通過將 any 型別改變為 unknown 型別,我們已將允許所有更改的默認設定,更改為禁止任何更改,

 

2.8 Tuple 型別

 

眾所周知,陣列一般由同種型別的值組成,但有時我們需要在單個變數中存盤不同型別的值,這時候我們就可以使用元組,在 JavaScript 中是沒有元組的,元組是 TypeScript 中特有的型別,其作業方式類似于陣列,

 

元組可用于定義具有有限數量的未命名屬性的型別,每個屬性都有一個關聯的型別,使用元組時,必須提供每個屬性的值,為了更直觀地理解元組的概念,我們來看一個具體的例子:

 

 let tupleType: [string, boolean];tupleType = ["Semlinker", true];

 

在上面代碼中,我們定義了一個名為 tupleType 的變數,它的型別是一個型別陣列 [string, boolean],然后我們按照正確的型別依次初始化 tupleType 變數,與陣列一樣,我們可以通過下標來訪問元組中的元素:

 

 console.log(tupleType[0]); // Semlinkerconsole.log(tupleType[1]); // true

 

在元組初始化的時候,如果出現型別不匹配的話,比如:

 

 tupleType = [true, "Semlinker"];

 

此時,TypeScript 編譯器會提示以下錯誤資訊:

 

 [0]: Type 'true' is not assignable to type 'string'.[1]: Type 'string' is not assignable to type 'boolean'.

 

很明顯是因為型別不匹配導致的,在元組初始化的時候,我們還必須提供每個屬性的值,不然也會出現錯誤,比如:

 

 tupleType = ["Semlinker"];

 

此時,TypeScript 編譯器會提示以下錯誤資訊:

 

 Property '1' is missing in type '[string]' but required in type '[string, boolean]'.

 

2.9 Void 型別

 

某種程度上來說,void 型別像是與 any 型別相反,它表示沒有任何型別,當一個函式沒有回傳值時,你通常會見到其回傳值型別是 void:

 

 // 宣告函式回傳值為voidfunction warnUser(): void {  console.log("This is my warning message");}

 

以上代碼編譯生成的 ES5 代碼如下:

 

 "use strict";function warnUser() {  console.log("This is my warning message");}

 

需要注意的是,宣告一個 void 型別的變數沒有什么作用,因為它的值只能為 undefinednull

 

 let unusable: void = undefined;

 

2.10 Null 和 Undefined 型別

 

TypeScript 里,undefinednull 兩者有各自的型別分別為 undefinednull

 

 let u: undefined = undefined;let n: null = null;

 

默認情況下 nullundefined 是所有型別的子型別, 就是說你可以把 nullundefined 賦值給 number 型別的變數,然而,如果你指定了--strictNullChecks 標記,nullundefined 只能賦值給 void 和它們各自的型別,

 

2.11 Never 型別

 

never 型別表示的是那些永不存在的值的型別, 例如,never 型別是那些總是會拋出例外或根本就不會有回傳值的函式運算式或箭頭函式運算式的回傳值型別,

 

 ?
 // 回傳never的函式必須存在無法達到的終點
 function error(message: string): never {
   throw new Error(message);
 }
 ?
 function infiniteLoop(): never {
   while (true) {}
 }

 

在 TypeScript 中,可以利用 never 型別的特性來實作全面性檢查,具體示例如下:

 

 ?
 type Foo = string | number;
 ?
 function controlFlowAnalysisWithNever(foo: Foo) {
   if (typeof foo === "string") {
     // 這里 foo 被收窄為 string 型別
  } else if (typeof foo === "number") {
     // 這里 foo 被收窄為 number 型別
  } else {
     // foo 在這里是 never
     const check: never = foo;
  }
 }

 

注意在 else 分支里面,我們把收窄為 never 的 foo 賦值給一個顯示宣告的 never 變數,如果一切邏輯正確,那么這里應該能夠編譯通過,但是假如后來有一天你的同事修改了 Foo 的型別:

 

 type Foo = string | number | boolean;

 

然而他忘記同時修改 controlFlowAnalysisWithNever 方法中的控制流程,這時候 else 分支的 foo 型別會被收窄為 boolean 型別,導致無法賦值給 never 型別,這時就會產生一個編譯錯誤,通過這個方式,我們可以確保

 

controlFlowAnalysisWithNever 方法總是窮盡了 Foo 的所有可能型別, 通過這個示例,我們可以得出一個結論:使用 never 避免出現新增了聯合型別沒有對應的實作,目的就是寫出型別絕對安全的代碼,

 

三、TypeScript 斷言

 

有時候你會遇到這樣的情況,你會比 TypeScript 更了解某個值的詳細資訊,通常這會發生在你清楚地知道一個物體具有比它現有型別更確切的型別,

 

通過型別斷言這種方式可以告訴編譯器,“相信我,我知道自己在干什么”,型別斷言好比其他語言里的型別轉換,但是不進行特殊的資料檢查和解構,它沒有運行時的影響,只是在編譯階段起作用,

 

型別斷言有兩種形式:

 

3.1 “尖括號” 語法

 

 ?
 let someValue: any = "this is a string";
 let strLength: number = (<string>someValue).length;

 

3.2 as 語法

 

 ?
 let someValue: any = "this is a string";
 let strLength: number = (someValue as string).length;

 

四、型別守衛

 

A type guard is some expression that performs a runtime check that guarantees the type in some scope. —— TypeScript 官方檔案

 

型別保護是可執行運行時檢查的一種運算式,用于確保該型別在一定的范圍內,換句話說,型別保護可以保證一個字串是一個字串,盡管它的值也可以是一個數值,型別保護與特性檢測并不是完全不同,其主要思想是嘗試檢測驗性、方法或原型,以確定如何處理值,目前主要有四種的方式來實作型別保護:

 

4.1 in 關鍵字

 

 ?
 interface Admin {
   name: string;
   privileges: string[];
 }
 ?
 interface Employee {
   name: string;
   startDate: Date;
 }
 ?
 type UnknownEmployee = Employee | Admin;
 ?
 function printEmployeeInformation(emp: UnknownEmployee) {
   console.log("Name: " + emp.name);
   if ("privileges" in emp) {
     console.log("Privileges: " + emp.privileges);
  }
   if ("startDate" in emp) {
     console.log("Start Date: " + emp.startDate);
  }
 }

 

4.2 typeof 關鍵字

 

 ?
 function padLeft(value: string, padding: string | number) {
   if (typeof padding === "number") {
       return Array(padding + 1).join(" ") + value;
  }
   if (typeof padding === "string") {
       return padding + value;
  }
   throw new Error(`Expected string or number, got '${padding}'.`);
 }

 

typeof 型別保護只支持兩種形式:typeof v === "typename"typeof v !== typename"typename" 必須是 "number""string""boolean""symbol", 但是 TypeScript 并不會阻止你與其它字串比較,語言不會把那些運算式識別為型別保護,

 

4.3 instanceof 關鍵字

 

 ?
 interface Padder {
   getPaddingString(): string;
 }
 ?
 class SpaceRepeatingPadder implements Padder {
   constructor(private numSpaces: number) {}
   getPaddingString() {
     return Array(this.numSpaces + 1).join(" ");
  }
 }
 ?
 class StringPadder implements Padder {
   constructor(private value: string) {}
   getPaddingString() {
     return this.value;
  }
 }
 ?
 let padder: Padder = new SpaceRepeatingPadder(6);
 ?
 if (padder instanceof SpaceRepeatingPadder) {
   // padder的型別收窄為 'SpaceRepeatingPadder'
 }

 

4.4 自定義型別保護的型別謂詞

 

 ?
 function isNumber(x: any): x is number {
   return typeof x === "number";
 }
 ?
 function isString(x: any): x is string {
   return typeof x === "string";
 }

 

五、聯合型別和型別別名

 

5.1 聯合型別

 

聯合型別通常與 nullundefined 一起使用:

 

 const sayHello = (name: string | undefined) => {  /* ... */};

 

例如,這里 name 的型別是 string | undefined 意味著可以將 stringundefined 的值傳遞給sayHello 函式,

 

 sayHello("Semlinker");sayHello(undefined);

 

通過這個示例,你可以憑直覺知道型別 A 和型別 B 聯合后的型別是同時接受 A 和 B 值的型別,

 

5.2 可辨識聯合

 

TypeScript 可辨識聯合(Discriminated Unions)型別,也稱為代數資料型別或標簽聯合型別,它包含 3 個要點:可辨識、聯合型別和型別守衛,

 

這種型別的本質是結合聯合型別和字面量型別的一種型別保護方法,如果一個型別是多個型別的聯合型別,且多個型別含有一個公共屬性,那么就可以利用這個公共屬性,來創建不同的型別保護區塊,

 

1.可辨識

 

可辨識要求聯合型別中的每個元素都含有一個單例型別屬性,比如:

 

 ?
 enum CarTransmission {
   Automatic = 200,
   Manual = 300
 }
 ?
 interface Motorcycle {
   vType: "motorcycle"; // discriminant
   make: number; // year
 }
 ?
 interface Car {
   vType: "car"; // discriminant
   transmission: CarTransmission
 }
 ?
 interface Truck {
   vType: "truck"; // discriminant
   capacity: number; // in tons
 }

 

在上述代碼中,我們分別定義了 MotorcycleCarTruck 三個介面,在這些介面中都包含一個 vType 屬性,該屬性被稱為可辨識的屬性,而其它的屬性只跟特性的介面相關,

 

2.聯合型別

 

基于前面定義了三個介面,我們可以創建一個 Vehicle 聯合型別:

 

 type Vehicle = Motorcycle | Car | Truck;

 

現在我們就可以開始使用 Vehicle 聯合型別,對于 Vehicle 型別的變數,它可以表示不同型別的車輛,

 

3.型別守衛

 

下面我們來定義一個 evaluatePrice 方法,該方法用于根據車輛的型別、容量和評估因子來計算價格,具體實作如下:

 

 ?
 const EVALUATION_FACTOR = Math.PI;
 function evaluatePrice(vehicle: Vehicle) {
   return vehicle.capacity * EVALUATION_FACTOR;
 }
 ?
 const myTruck: Truck = { vType: "truck", capacity: 9.5 };
 evaluatePrice(myTruck);

 

對于以上代碼,TypeScript 編譯器將會提示以下錯誤資訊:

 

 ?
 Property 'capacity' does not exist on type 'Vehicle'.
 Property 'capacity' does not exist on type 'Motorcycle'.

 

原因是在 Motorcycle 介面中,并不存在 capacity 屬性,而對于 Car 介面來說,它也不存在 capacity 屬性,那么,現在我們應該如何解決以上問題呢?這時,我們可以使用型別守衛,下面我們來重構一下前面定義的 evaluatePrice 方法,重構后的代碼如下:

 

 ?
 function evaluatePrice(vehicle: Vehicle) {
   switch(vehicle.vType) {
     case "car":
       return vehicle.transmission * EVALUATION_FACTOR;
     case "truck":
       return vehicle.capacity * EVALUATION_FACTOR;
     case "motorcycle":
       return vehicle.make * EVALUATION_FACTOR;
  }
 }

 

在以上代碼中,我們使用 switchcase 運算子來實作型別守衛,從而確保在 evaluatePrice 方法中,我們可以安全地訪問 vehicle 物件中的所包含的屬性,來正確的計算該車輛型別所對應的價格,

 

5.3 型別別名

 

型別別名用來給一個型別起個新名字,

 

 type Message = string | string[];let greet = (message: Message) => {  // ...};

 

六、交叉型別

 

TypeScript 交叉型別是將多個型別合并為一個型別, 這讓我們可以把現有的多種型別疊加到一起成為一種型別,它包含了所需的所有型別的特性,

 

 ?
 interface IPerson {
   id: string;
   age: number;
 }
 ?
 interface IWorker {
   companyId: string;
 }
 ?
 type IStaff = IPerson & IWorker;
 ?
 const staff: IStaff = {
   id: 'E1006',
   age: 33,
   companyId: 'EFT'
 };
 ?
 console.dir(staff)

 

在上面示例中,我們首先為 IPerson 和 IWorker 型別定義了不同的成員,然后通過 & 運算子定義了 IStaff 交叉型別,所以該型別同時擁有 IPerson 和 IWorker 這兩種型別的成員,

 

七、TypeScript 函式

 

7.1 TypeScript 函式與 JavaScript 函式的區別

img

 

7.2 箭頭函式

 

1.常見語法

 

 ?
 myBooks.forEach(() => console.log('reading'));
 ?
 myBooks.forEach(title => console.log(title));
 ?
 myBooks.forEach((title, idx, arr) =>
   console.log(idx + '-' + title);
 );
 ?
 myBooks.forEach((title, idx, arr) => {
   console.log(idx + '-' + title);
 });

 

2.使用示例

 

 ?
 // 未使用箭頭函式
 function Book() {
   let self = this;
   self.publishDate = 2016;
   setInterval(function () {
     console.log(self.publishDate);
  }, 1000);
 }
 ?
 // 使用箭頭函式
 function Book() {
   this.publishDate = 2016;
   setInterval(() => {
     console.log(this.publishDate);
  }, 1000);
 }

 

7.3 引數型別和回傳型別

 

 ?
 function createUserId(name: string, id: number): string {
   return name + id;
 }

 

7.4 函式型別

 

 ?
 let IdGenerator: (chars: string, nums: number) => string;
 ?
 function createUserId(name: string, id: number): string {
   return name + id;
 }
 ?
 IdGenerator = createUserId;

 

7.5 可選引數及默認引數

 

 ?
 // 可選引數
 function createUserId(name: string, id: number, age?: number): string {
   return name + id;
 }
 ?
 // 默認引數
 function createUserId(
   name: string = "Semlinker",
   id: number,
   age?: number
 ): string {
   return name + id;
 }

 

在宣告函式時,可以通過 ? 號來定義可選引數,比如 age?: number 這種形式,在實際使用時,需要注意的是可選引數要放在普通引數的后面,不然會導致編譯錯誤,

 

7.6 剩余引數

 

 ?
 function push(array, ...items) {
   items.forEach(function (item) {
     array.push(item);
  });
 }
 ?
 let a = [];
 push(a, 1, 2, 3);

 

7.7 函式多載

 

函式多載或方法多載是使用相同名稱和不同引數數量或型別創建多個方法的一種能力,要解決前面遇到的問題,方法就是為同一個函式提供多個函式型別定義來進行函式多載,編譯器會根據這個串列去處理函式的呼叫,

 

 ?
 function add(a: number, b: number): number;
 function add(a: string, b: string): string;
 function add(a: string, b: number): string;
 function add(a: number, b: string): string;
 function add(a: Combinable, b: Combinable) {
   if (typeof a === "string" || typeof b === "string") {
     return a.toString() + b.toString();
  }
   return a + b;
 }

 

在以上代碼中,我們為 add 函式提供了多個函式型別定義,從而實作函式的多載,之后,可惡的錯誤訊息又消失了,因為這時 result 變數的型別是 string 型別,在 TypeScript 中除了可以多載普通函式之外,我們還可以多載類中的成員方法,

 

方法多載是指在同一個類中方法同名,引數不同(引數型別不同、引數個數不同或引數個數相同時引數的先后順序不同),呼叫時根據實參的形式,選擇與它匹配的方法執行操作的一種技術,所以類中成員方法滿足多載的條件是:在同一個類中,方法名相同且引數串列不同,下面我們來舉一個成員方法多載的例子:

 

 ?
 class Calculator {
   add(a: number, b: number): number;
   add(a: string, b: string): string;
   add(a: string, b: number): string;
   add(a: number, b: string): string;
   add(a: Combinable, b: Combinable) {
     if (typeof a === "string" || typeof b === "string") {
       return a.toString() + b.toString();
    }
     return a + b;
  }
 }
 ?
 const calculator = new Calculator();
 const result = calculator.add("Semlinker", " Kakuqo");

 

這里需要注意的是,當 TypeScript 編譯器處理函式多載時,它會查找多載串列,嘗試使用第一個多載定義, 如果匹配的話就使用這個, 因此,在定義多載的時候,一定要把最精確的定義放在最前面,另外在 Calculator 類中,add(a: Combinable, b: Combinable){ } 并不是多載串列的一部分,因此對于 add 成員方法來說,我們只定義了四個多載方法,

 

八、TypeScript 陣列

 

8.1 陣列解構

 

 ?
 let x: number; let y: number; let z: number;
 let five_array = [0,1,2,3,4];
 [x,y,z] = five_array;

 

8.2 陣列展開運算子

 

 let two_array = [0, 1];
 let five_array = [...two_array, 2, 3, 4];

 

8.3 陣列遍歷

 

 let colors: string[] = ["red", "green", "blue"];
 for (let i of colors) {  
  console.log(i);
 }

 

九、TypeScript 物件

 

9.1 物件解構

 

 ?
 let person = {
   name: "Semlinker",
   gender: "Male",
 };
 ?
 let { name, gender } = person;

 

9.2 物件展開運算子

 

 ?
 let person = {
   name: "Semlinker",
   gender: "Male",
   address: "Xiamen",
 };
 ?
 // 組裝物件
 let personWithAge = { ...person, age: 33 };
 ?
 // 獲取除了某些項外的其它項
 let { name, ...rest } = person;

 

十、TypeScript 介面

 

在面向物件語言中,介面是一個很重要的概念,它是對行為的抽象,而具體如何行動需要由類去實作,

 

TypeScript 中的介面是一個非常靈活的概念,除了可用于對類的一部分行為進行抽象以外,也常用于對「物件的形狀(Shape)」進行描述,

 

10.1 物件的形狀

 

 ?
 interface Person {
   name: string;
   age: number;
 }
 ?
 let Semlinker: Person = {
   name: "Semlinker",
   age: 33,
 };

 

10.2 可選 | 只讀屬性

 

 ?
 interface Person {
   readonly name: string;
   age?: number;
 }

 

只讀屬性用于限制只能在物件剛剛創建的時候修改其值,此外 TypeScript 還提供了 ReadonlyArray 型別,它與 Array 相似,只是把所有可變方法去掉了,因此可以確保陣列創建后再也不能被修改,

 

 ?
 let a: number[] = [1, 2, 3, 4];
 let ro: ReadonlyArray<number> = a;
 ro[0] = 12; // error!
 ro.push(5); // error!
 ro.length = 100; // error!
 a = ro; // error!

 

十一、TypeScript 類

 

11.1 類的屬性與方法

 

在面向物件語言中,類是一種面向物件計算機編程語言的構造,是創建物件的藍圖,描述了所創建的物件共同的屬性和方法,

 

在 TypeScript 中,我們可以通過 Class 關鍵字來定義一個類:

 

 ?
 class Greeter {
   // 靜態屬性
   static cname: string = "Greeter";
   // 成員屬性
   greeting: string;
 ?
   // 建構式 - 執行初始化操作
   constructor(message: string) {
     this.greeting = message;
  }
 ?
   // 靜態方法
   static getClassName() {
     return "Class name is Greeter";
  }
 ?
   // 成員方法
   greet() {
     return "Hello, " + this.greeting;
  }
 }
 ?
 let greeter = new Greeter("world");

 

那么成員屬性與靜態屬性,成員方法與靜態方法有什么區別呢?這里無需過多解釋,我們直接看一下以下編譯生成的 ES5 代碼:

 

 ?
 "use strict";
 var Greeter = /** @class */ (function () {
     // 建構式 - 執行初始化操作
     function Greeter(message) {
         this.greeting = message;
    }
     // 靜態方法
     Greeter.getClassName = function () {
         return "Class name is Greeter";
    };
     // 成員方法
     Greeter.prototype.greet = function () {
         return "Hello, " + this.greeting;
    };
     // 靜態屬性
     Greeter.cname = "Greeter";
     return Greeter;
 }());
 var greeter = new Greeter("world");

 

11.2 訪問器

 

在 TypeScript 中,我們可以通過 gettersetter 方法來實作資料的封裝和有效性校驗,防止出現例外資料,

 

 ?
 let passcode = "Hello TypeScript";
 ?
 class Employee {
   private _fullName: string;
 ?
   get fullName(): string {
     return this._fullName;
  }
 ?
   set fullName(newName: string) {
     if (passcode && passcode == "Hello TypeScript") {
       this._fullName = newName;
    } else {
       console.log("Error: Unauthorized update of employee!");
    }
  }
 }
 ?
 let employee = new Employee();
 employee.fullName = "Semlinker";
 if (employee.fullName) {
   console.log(employee.fullName);
 }

 

11.3 類的繼承

 

繼承 (Inheritance) 是一種聯結類與類的層次模型,指的是一個類(稱為子類、子介面)繼承另外的一個類(稱為父類、父介面)的功能,并可以增加它自己的新功能的能力,繼承是類與類或者介面與介面之間最常見的關系,

 

繼承是一種 is-a 關系:

 

img

 

在 TypeScript 中,我們可以通過 extends 關鍵字來實作繼承:

 

 ?
 class Animal {
   name: string;
   
   constructor(theName: string) {
     this.name = theName;
  }
   
   move(distanceInMeters: number = 0) {
     console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
 }
 ?
 class Snake extends Animal {
   constructor(name: string) {
     super(name);
  }
   
   move(distanceInMeters = 5) {
     console.log("Slithering...");
     super.move(distanceInMeters);
  }
 }
 ?
 let sam = new Snake("Sammy the Python");
 sam.move();

 

11.4 ECMAScript 私有欄位

 

在 TypeScript 3.8 版本就開始支持ECMAScript 私有欄位,使用方式如下:

 

 ?
 class Person {
   #name: string;
 ?
   constructor(name: string) {
     this.#name = name;
  }
 ?
   greet() {
     console.log(`Hello, my name is ${this.#name}!`);
  }
 }
 ?
 let semlinker = new Person("Semlinker");
 ?
 semlinker.#name;
 //     ~~~~~
 // Property '#name' is not accessible outside class 'Person'
 // because it has a private identifier.

 

與常規屬性(甚至使用 private 修飾符宣告的屬性)不同,私有欄位要牢記以下規則:

 

  • 私有欄位以 # 字符開頭,有時我們稱之為私有名稱;

  • 每個私有欄位名稱都唯一地限定于其包含的類;

  • 不能在私有欄位上使用 TypeScript 可訪問性修飾符(如 public 或 private);

  • 私有欄位不能在包含的類之外訪問,甚至不能被檢測到,

 

十二、TypeScript 泛型

 

軟體工程中,我們不僅要創建一致的定義良好的 API,同時也要考慮可重用性, 組件不僅能夠支持當前的資料型別,同時也能支持未來的資料型別,這在創建大型系統時為你提供了十分靈活的功能,

 

在像 C# 和 Java 這樣的語言中,可以使用泛型來創建可重用的組件,一個組件可以支持多種型別的資料, 這樣用戶就可以以自己的資料型別來使用組件,

 

設計泛型的關鍵目的是在成員之間提供有意義的約束,這些成員可以是:類的實體成員、類的方法、函式引數和函式回傳值,

 

泛型(Generics)是允許同一個函式接受不同型別引數的一種模板,相比于使用 any 型別,使用泛型來創建可復用的組件要更好,因為泛型會保留引數型別,

 

12.1 泛型介面

 

 interface GenericIdentityFn<T> {  (arg: T): T;}

 

12.2 泛型類

 

 ?
 class GenericNumber<T> {
   zeroValue: T;
   add: (x: T, y: T) => T;
 }
 ?
 let myGenericNumber = new GenericNumber<number>();
 myGenericNumber.zeroValue = 0;
 myGenericNumber.add = function (x, y) {
   return x + y;
 };

 

12.3 泛型變數

 

對剛接觸 TypeScript 泛型的小伙伴來說,看到 T 和 E,還有 K 和 V 這些泛型變數時,估計會一臉懵逼,其實這些大寫字母并沒有什么本質的區別,只不過是一個約定好的規范而已,也就是說使用大寫字母 A-Z 定義的型別變數都屬于泛型,把 T 換成 A,也是一樣的,下面我們介紹一下一些常見泛型變數代表的意思:

 

  • T(Type):表示一個 TypeScript 型別

  • K(Key):表示物件中的鍵型別

  • V(Value):表示物件中的值型別

  • E(Element):表示元素型別

 

12.4 泛型工具型別

 

為了方便開發者 TypeScript 內置了一些常用的工具型別,比如 Partial、Required、Readonly、Record 和 ReturnType 等,出于篇幅考慮,這里我們只簡單介紹 Partial 工具型別,不過在具體介紹之前,我們得先介紹一些相關的基礎知識,方便讀者自行學習其它的工具型別,

 

1.typeof

 

在 TypeScript 中,typeof 運算子可以用來獲取一個變數宣告或物件的型別,

 

 ?
 interface Person {
   name: string;
   age: number;
 }
 ?
 const sem: Person = { name: 'semlinker', age: 30 };
 type Sem= typeof sem; // -> Person
 ?
 function toArray(x: number): Array<number> {
   return [x];
 }
 ?
 type Func = typeof toArray; // -> (x: number) => number[]

 

2.keyof

 

keyof 運算子可以用來一個物件中的所有 key 值:

 

 ?
 interface Person {
     name: string;
     age: number;
 }
 ?
 type K1 = keyof Person; // "name" | "age"
 type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
 type K3 = keyof { [x: string]: Person };  // string | number

 

3.in

 

in 用來遍歷列舉型別:

 

 ?
 type Keys = "a" | "b" | "c"
 ?
 type Obj = {
  [p in Keys]: any
 } // -> { a: any, b: any, c: any }

 

4.infer

 

在條件型別陳述句中,可以用 infer 宣告一個型別變數并且對它進行使用,

 

 ?
 type ReturnType<T> = T extends (
   ...args: any[]
 ) => infer R ? R : any;

 

以上代碼中 infer R 就是宣告一個變數來承載傳入函式簽名的回傳值型別,簡單說就是用它取到函式回傳值的型別方便之后使用,

 

5.extends

 

有時候我們定義的泛型不想過于靈活或者說想繼承某些類等,可以通過 extends 關鍵字添加泛型約束,

 

 ?
 interface ILengthwise {
   length: number;
 }
 ?
 function loggingIdentity<T extends ILengthwise>(arg: T): T {
   console.log(arg.length);
   return arg;
 }

 

現在這個泛型函式被定義了約束,因此它不再是適用于任意型別:

 

 loggingIdentity(3);  // Error, number doesn't have a .length property

 

這時我們需要傳入符合約束型別的值,必須包含必須的屬性:

 

 loggingIdentity({length: 10, value: 3});

 

6.Partial

 

Partial 的作用就是將某個型別里的屬性全部變為可選項 ?

 

定義:

 

 ?
 /**
  * node_modules/typescript/lib/lib.es5.d.ts
  * Make all properties in T optional
  */
 type Partial<T> = {
  [P in keyof T]?: T[P];
 };

 

在以上代碼中,首先通過 keyof T 拿到 T 的所有屬性名,然后使用 in 進行遍歷,將值賦給 P,最后通過 T[P] 取得相應的屬性值,中間的 ? 號,用于將所有屬性變為可選,

 

示例:

 

 ?
 interface Todo {
   title: string;
   description: string;
 }
 ?
 function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
   return { ...todo, ...fieldsToUpdate };
 }
 ?
 const todo1 = {
   title: "organize desk",
   description: "clear clutter",
 };
 ?
 const todo2 = updateTodo(todo1, {
   description: "throw out trash",
 });

 

在上面的 updateTodo 方法中,我們利用 Partial 工具型別,定義 fieldsToUpdate 的型別為 Partial,即:

 

 {   title?: string | undefined;   description?: string | undefined;}

 

十三、TypeScript 裝飾器

 

13.1 裝飾器是什么

 

  • 它是一個運算式

  • 該運算式被執行后,回傳一個函式

  • 函式的入參分別為 target、name 和 descriptor

  • 執行該函式后,可能回傳 descriptor 物件,用于配置 target 物件

 

13.2 裝飾器的分類

 

  • 類裝飾器(Class decorators)

  • 屬性裝飾器(Property decorators)

  • 方法裝飾器(Method decorators)

  • 引數裝飾器(Parameter decorators)

 

13.3 類裝飾器

 

類裝飾器宣告:

 

 ?
 declare type ClassDecorator = <TFunction extends Function>(
   target: TFunction
 ) => TFunction | void;

 

類裝飾器顧名思義,就是用來裝飾類的,它接收一個引數:

 

  • target: TFunction - 被裝飾的類

 

看完第一眼后,是不是感覺都不好了,沒事,我們馬上來個例子:

 

 ?
 function Greeter(target: Function): void {
   target.prototype.greet = function (): void {
     console.log("Hello Semlinker!");
  };
 }
 ?
 @Greeter
 class Greeting {
   constructor() {
     // 內部實作
  }
 }
 ?
 let myGreeting = new Greeting();
 myGreeting.greet(); // console output: 'Hello Semlinker!';

 

上面的例子中,我們定義了 Greeter 類裝飾器,同時我們使用了 @Greeter 語法糖,來使用裝飾器,

 

友情提示:讀者可以直接復制上面的代碼,在 TypeScript Playground 中運行查看結果,

 

有的讀者可能想問,例子中總是輸出 Hello Semlinker! ,能自定義輸出的問候語么 ?這個問題很好,答案是可以的,

 

具體實作如下:

 

 ?
 function Greeter(greeting: string) {
   return function (target: Function) {
     target.prototype.greet = function (): void {
       console.log(greeting);
    };
  };
 }
 ?
 @Greeter("Hello TS!")
 class Greeting {
   constructor() {
     // 內部實作
  }
 }
 ?
 let myGreeting = new Greeting();
 myGreeting.greet(); // console output: 'Hello TS!';

 

13.4 屬性裝飾器

 

屬性裝飾器宣告:

 

 ?
 declare type PropertyDecorator = (target:Object,
   propertyKey: string | symbol ) => void;

 

屬性裝飾器顧名思義,用來裝飾類的屬性,它接收兩個引數:

 

  • target: Object - 被裝飾的類

  • propertyKey: string | symbol - 被裝飾類的屬性名

 

趁熱打鐵,馬上來個例子熱熱身:

 

 ?
 function logProperty(target: any, key: string) {
   delete target[key];
 ?
   const backingField = "_" + key;
 ?
   Object.defineProperty(target, backingField, {
     writable: true,
     enumerable: true,
     configurable: true
  });
 ?
   // property getter
   const getter = function (this: any) {
     const currVal = this[backingField];
     console.log(`Get: ${key} => ${currVal}`);
     return currVal;
  };
 ?
   // property setter
   const setter = function (this: any, newVal: any) {
     console.log(`Set: ${key} => ${newVal}`);
     this[backingField] = newVal;
  };
 ?
   // Create new property with getter and setter
   Object.defineProperty(target, key, {
     get: getter,
     set: setter,
     enumerable: true,
     configurable: true
  });
 }
 ?
 class Person {
   @logProperty
   public name: string;
 ?
   constructor(name : string) {
     this.name = name;
  }
 }
 ?
 const p1 = new Person("semlinker");
 p1.name = "kakuqo";

 

以上代碼我們定義了一個 logProperty 函式,來跟蹤用戶對屬性的操作,當代碼成功運行后,在控制臺會輸出以下結果:

 

 Set: name => semlinkerSet: name => kakuqo

 

13.5 方法裝飾器

 

方法裝飾器宣告:

 

 ?
 declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol,
   descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;

 

方法裝飾器顧名思義,用來裝飾類的方法,它接收三個引數:

 

  • target: Object - 被裝飾的類

  • propertyKey: string | symbol - 方法名

  • descriptor: TypePropertyDescript - 屬性描述符

 

廢話不多說,直接上例子:

 

 ?
 function LogOutput(tarage: Function, key: string, descriptor: any) {
   let originalMethod = descriptor.value;
   let newMethod = function(...args: any[]): any {
     let result: any = originalMethod.apply(this, args);
     if(!this.loggedOutput) {
       this.loggedOutput = new Array<any>();
    }
     this.loggedOutput.push({
       method: key,
       parameters: args,
       output: result,
       timestamp: new Date()
    });
     return result;
  };
   descriptor.value = newMethod;
 }
 ?
 class Calculator {
   @LogOutput
   double (num: number): number {
     return num * 2;
  }
 }
 ?
 let calc = new Calculator();
 calc.double(11);
 // console ouput: [{method: "double", output: 22, ...}]
 console.log(calc.loggedOutput);

 

下面我們來介紹一下引數裝飾器,

 

13.6 引數裝飾器

 

引數裝飾器宣告:

 

 ?
 declare type ParameterDecorator = (target: Object, propertyKey: string | symbol,
   parameterIndex: number ) => void

 

引數裝飾器顧名思義,是用來裝飾函式引數,它接收三個引數:

 

  • target: Object - 被裝飾的類

  • propertyKey: string | symbol - 方法名

  • parameterIndex: number - 方法中引數的索引值

 

 function Log(target: Function, key: string, parameterIndex: number) {  let functionLogged = key || target.prototype.constructor.name;  console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has    been decorated`);}class Greeter {  greeting: string;  constructor(@Log phrase: string) {    this.greeting = phrase;   }}// console output: The parameter in position 0 // at Greeter has been decorated

 

介紹完 TypeScript 入門相關的基礎知識,猜測很多剛入門的小伙伴已有 “從入門到放棄” 的想法,最后我們來簡單介紹一下編譯背景關系,

 

十四、編譯背景關系

 

14.1 tsconfig.json 的作用

 

  • 用于標識 TypeScript 專案的根路徑;

  • 用于配置 TypeScript 編譯器;

  • 用于指定編譯的檔案,

 

14.2 tsconfig.json 重要欄位

 

  • files - 設定要編譯的檔案的名稱;

  • include - 設定需要進行編譯的檔案,支持路徑模式匹配;

  • exclude - 設定無需進行編譯的檔案,支持路徑模式匹配;

  • compilerOptions - 設定與編譯流程相關的選項,

 

14.3 compilerOptions 選項

 

compilerOptions 支持很多選項,常見的有 baseUrltargetbaseUrlmoduleResolutionlib 等,

 

compilerOptions 每個選項的詳細說明如下:

 

 ?
 {
   "compilerOptions": {
 ?
     /* 基本選項 */
     "target": "es5",                       // 指定 ECMAScript 目標版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
     "module": "commonjs",                  // 指定使用模塊: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
     "lib": [],                             // 指定要包含在編譯中的庫檔案
     "allowJs": true,                       // 允許編譯 javascript 檔案
     "checkJs": true,                       // 報告 javascript 檔案中的錯誤
     "jsx": "preserve",                     // 指定 jsx 代碼的生成: 'preserve', 'react-native', or 'react'
     "declaration": true,                   // 生成相應的 '.d.ts' 檔案
     "sourceMap": true,                     // 生成相應的 '.map' 檔案
     "outFile": "./",                       // 將輸出檔案合并為一個檔案
     "outDir": "./",                        // 指定輸出目錄
     "rootDir": "./",                       // 用來控制輸出目錄結構 --outDir.
     "removeComments": true,                // 洗掉編譯后的所有的注釋
     "noEmit": true,                        // 不生成輸出檔案
     "importHelpers": true,                 // 從 tslib 匯入輔助工具函式
     "isolatedModules": true,               // 將每個檔案做為單獨的模塊 (與 'ts.transpileModule' 類似).
 ?
     /* 嚴格的型別檢查選項 */
     "strict": true,                        // 啟用所有嚴格型別檢查選項
     "noImplicitAny": true,                 // 在運算式和宣告上有隱含的 any型別時報錯
     "strictNullChecks": true,              // 啟用嚴格的 null 檢查
     "noImplicitThis": true,                // 當 this 運算式值為 any 型別的時候,生成一個錯誤
     "alwaysStrict": true,                  // 以嚴格模式檢查每個模塊,并在每個檔案里加入 'use strict'
 ?
     /* 額外的檢查 */
     "noUnusedLocals": true,                // 有未使用的變數時,拋出錯誤
     "noUnusedParameters": true,            // 有未使用的引數時,拋出錯誤
     "noImplicitReturns": true,             // 并不是所有函式里的代碼都有回傳值時,拋出錯誤
     "noFallthroughCasesInSwitch": true,    // 報告 switch 陳述句的 fallthrough 錯誤,(即,不允許 switch 的 case 陳述句貫穿)
 ?
     /* 模塊決議選項 */
     "moduleResolution": "node",            // 選擇模塊決議策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
     "baseUrl": "./",                       // 用于決議非相對模塊名稱的基目錄
     "paths": {},                           // 模塊名到基于 baseUrl 的路徑映射的串列
     "rootDirs": [],                        // 根檔案夾串列,其組合內容表示專案運行時的結構內容
     "typeRoots": [],                       // 包含型別宣告的檔案串列
     "types": [],                           // 需要包含的型別宣告檔案名串列
     "allowSyntheticDefaultImports": true,  // 允許從沒有設定默認匯出的模塊中默認匯入,
 ?
     /* Source Map Options */
     "sourceRoot": "./",                    // 指定除錯器應該找到 TypeScript 檔案而不是源檔案的位置
     "mapRoot": "./",                       // 指定除錯器應該找到映射檔案而不是生成檔案的位置
     "inlineSourceMap": true,               // 生成單個 soucemaps 檔案,而不是將 sourcemaps 生成不同的檔案
     "inlineSources": true,                 // 將代碼與 sourcemaps 生成到一個檔案中,要求同時設定了 --inlineSourceMap 或 --sourceMap 屬性
 ?
     /* 其他選項 */
     "experimentalDecorators": true,        // 啟用裝飾器
     "emitDecoratorMetadata": true          // 為裝飾器提供元資料的支持
  }
 }

總結

對了,小編為大家準備了一套2020最新的web前端資料,需要點擊下方鏈接獲取方式

學習前端,你掌握這些,二線也能輕松拿8K以上

 

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/49997.html

標籤:JavaScript

上一篇:開發人員都應該了解的 7 種 JavaScript 設計模式

下一篇:JavaScript簡介與常用輸出輸出方式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more