主頁 > 企業開發 > Typescript學習總結

Typescript學習總結

2021-06-27 06:26:28 企業開發

typescript(以下簡稱TS)出來也有好長時間了,下面記錄一下學習心得,

首先學這門語言前,請確保有以下基礎知識:

  • 扎實的javascript基礎知識
  • es6的基礎知識
  • 面向物件編程的概念(沒有也可以,就當是重新學一遍了)

接下來看一下TS的一些概念:

一、基本型別

TS的基礎型別有:字串(string)、數字(number)、布林值(boolean)、空(null)、未定義(undefined)、陣列(array)、物件(object)、元組(tuple)、列舉(enum)、any、void、never等12種,

寫法為在變數后加冒號然后跟變數型別的方式,例如:

1.字串

寫法:

let str: string = 'str';

2.數字

寫法:

let num: number = 123;

3.布林值

寫法:

let bol: boolean = false;

4.null

寫法:

let n: null = null;

5.undefined

寫法:

let u: undefined = undefined;

6.陣列

寫法:

let arr: number[] = [1,23,4,];
let arr1: Array<number> = [1,2,3];// 使用泛型的方式宣告變數

7.物件

寫法:

let obj: object={};

8.元組

寫法:

let tuple: [number,string] = [12,'3'];

9.列舉

寫法:

enum Num{
  one=1,// 從幾開始,默認為從0開始
  two,// 2
  three// 3
};

10.any

寫法:

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;
let anyArr: any = [1,2,'4',false,null];

11.viod

寫法:

function warnUser(): void {
    console.log("This is my warning message");
}
let unusable: void = undefined;
let unuse: void;

12.never

寫法:

function error(message: string): never {
    throw new Error(message);
}
// 推斷的回傳值型別為never
function fail() {
    return error("Something failed");
}
// 回傳never的函式必須存在無法達到的終點
function infiniteLoop(): never {
    while (true) {
    }
}

PS:型別斷言:如果你很清楚一個變數比它現有型別更確切的型別,那么你可以使用型別斷言,

型別斷言有兩種形式:

1.尖括號寫法:

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

2.As寫法:

let someValueTwo: any = "this is a string";
let strLengthTwo: number = (someValueTwo as string).length;

當在TypeScript里使用JSX時,只能使用As語法斷言,

2、介面

TypeScript的核心原則之一是對值所具有的結構進行型別檢查, 它有時被稱做“鴨式辨型法”或“結構性子型別化”, 在TypeScript里,介面的作用就是為這些型別命名定義契約,

寫法:

interface 介面名 { attribute: type }

示例:

interface LabelledValue {
    label: string;
}
function printLabel(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

1.可選屬性

interface SquareConfig {
    color?: string;
    width?: number;
}

2.只讀屬性

interface Point {
    readonly x: number;
    readonly y: number;
}

3.只讀陣列

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!
// a = ro as number[]; 用斷言修改陣列為可修改!

 4.跳過額外的屬性檢查

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
    return {
        color: 'blue',
        area:23
    }
    // ...
}
(方法1)
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
(方法2)索引簽名
interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}
(方法3)將這個物件賦值給一個另一個變數: 因為squareOptions不會經過額外屬性檢查
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

5.通過介面定義函式型別

interface SearchFunc {
    (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    let result = source.search(subString);
    return result > -1;
}
// or
// mySearch = function(src: string, sub: string): boolean {
//     let result = src.search(sub);
//     return result > -1;
// }

6.可索引的型別

TypeScript支持兩種索引簽名:字串和數字,

可以同時使用兩種型別的索引,但是數字索引的回傳值必須是字串索引回傳值型別的子型別, 這是因為當使用number來索引時,JavaScript會將它轉換成string然后再去索引物件, 也就是說用 100(一個number)去索引等同于使用"100"(一個string)去索引,因此兩者需要保持一致,

interface StringArray {
    [index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
// 定義的StringArray介面,它具有索引簽名,表示當用number去索引StringArray時會得到string型別的回傳值,
interface NumberDictionary {
    [index: string]: number;
    length: number;    // 可以,length是number型別
    name: string       // 錯誤,`name`的型別與索引型別回傳值的型別不匹配
}
// 將索引簽名設定為只讀
interface ReadonlyStringArray {
    readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!

7.實作介面

TypeScript也能夠用它來明確的強制一個類去符合某種契約

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}
class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

類靜態部分與實體部分的區別

當你操作類和介面的時候,你要知道類是具有兩個型別的:靜態部分的型別和實體的型別, 你會注意到,當你用構造器簽名去定義一個介面并試圖定義一個類去實作這個介面時會得到一個錯誤:

interface ClockConstructor {
    new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
    currentTime: Date;
    constructor(h: number, m: number) { }
}
// 這里因為當一個類實作了一個介面時,只對其實體部分進行型別檢查, constructor存在于類的靜態部分,所以不在檢查的范圍內,
// 因此,我們應該直接操作類的靜態部分, 看下面的例子,我們定義了兩個介面, ClockConstructor為建構式所用和ClockInterface為實體方法所用, 為了方便我們定義一個建構式 createClock,它用傳入的型別創建實體,
interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
// 因為createClock的第一個引數是ClockConstructor型別,在createClock(AnalogClock, 7, 32)里,會檢查AnalogClock是否符合建構式簽名,

8.繼承介面

和類一樣,介面也可以相互繼承, 這讓我們能夠從一個介面里復制成員到另一個介面里,可以更靈活地將介面分割到可重用的模塊里,

interface Shape {
    color: string;
}
interface Square extends Shape {
    sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;

繼承多個介面:

interface Shape {
    color: string;
}
interface PenStroke {
    penWidth: number;
}
interface Square extends Shape, PenStroke {
    sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

9.混合型別

一個物件可以同時做為函式和物件使用,并帶有額外的屬性,

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}
function getCounter(): Counter {
    let counter = <Counter>function (start: number) { console.log(start) };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

10.介面繼承類

當介面繼承了一個型別別時,它會繼承類的成員但不包括其實作,就好像介面宣告了所有類中存在的成員,但并沒有提供具體實作一樣, 介面同樣會繼承到類的private和protected成員, 這意味著當你創建了一個介面繼承了一個擁有私有或受保護的成員的類時,這個介面型別只能被這個類或其子類所實作(implement),

當你有一個龐大的繼承結構時這很有用,但要指出的是你的代碼只在子類擁有特定屬性時起作用, 這個子類除了繼承至基類外與基類沒有任何關系, 例:

class Control {
    private state: any;
}
interface SelectableControl extends Control {
    select(): void;
}
class Button extends Control implements SelectableControl {
    select() { }
}
class TextBox extends Control {
    select() { }
}
// 錯誤:“Image”型別缺少“state”屬性,
class Image implements SelectableControl {
    select() { }
}
class Location {

}
// 在上面的例子里,SelectableControl包含了Control的所有成員,包括私有成員state, 因為state是私有成員,所以只能夠是Control的子類們才能實作SelectableControl介面, 
因為只有 Control的子類才能夠擁有一個宣告于Control的私有成員state,這對私有成員的兼容性是必需的,
// 在Control類內部,是允許通過SelectableControl的實體來訪問私有成員state的, 實際上, SelectableControl介面和擁有select方法的Control類是一樣的,
Button和TextBox類是SelectableControl的子類(因為它們都繼承自Control并有select方法),但Image和Location類并不是這樣的,

 3.TS類

從ECMAScript 2015,也就是ES 6開始,JavaScript程式員將能夠使用基于類的面向物件的方式,

1.類宣告

class CreateClass {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
let greeter = new CreateClass('demo');

2.類繼承

2.1類繼承:類從基類中繼承了屬性和方法,這里,Dog是一個派生類,它派生自ParentClass基類,通過extends關鍵字,派生類通常被稱作子類,基類通常被稱作超類,

class ParentClass {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}
class Dog extends ParentClass {
    bark() {
        console.log('Woof! Woof!');
    }
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

2.2類私有屬性:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
    constructor() { super("Rhino"); }
}
class Employee1 {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee1("Bob");
console.log(animal.name); // 錯誤

2.3類受保護屬性:

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}
class Employee extends Person {
    private department: string;
    constructor(name: string, department: string) {
        super(name)
        this.department = department;
    }
    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 錯誤

建構式也可以被標記成 protected, 這意味著這個類不能在包含它的類外被實體化,但是能被繼承,比如:

class Person2 {
    protected name: string;
    protected constructor(theName: string) { this.name = theName; }
}
// Employee 能夠繼承 Person
class Employee2 extends Person {
    private department: string;
    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }
    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}
let howard2 = new Employee2("Howard", "Sales");
let john = new Person2("John"); // 錯誤: 'Person' 的建構式是被保護的.

2.4靜態屬性:類的靜態成員,這些屬性存在于類本身上面而不是類的實體上,

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

let grid1 = new Grid(1.0);  // 1x scale
let grid2 = new Grid(5.0);  // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

2.5抽象類:抽象類做為其它派生類的基類使用, 它們一般不會直接被實體化,

abstract class AbstractClass {
    abstract makeSound(): void;
    move(): void {
        console.log('roaming the earch...');
    }
}

抽象類中的抽象方法不包含具體實作且必須在派生類中實作, 抽象方法的語法與介面方法相似, 兩者都是定義方法簽名但不包含方法體, 然而,抽象方法必須包含 abstract關鍵字并且可以包含訪問修飾符,

abstract class Department {
    constructor(public name: string) {
    }
    printName(): void {
        console.log('Department name: ' + this.name);
    }
    abstract printMeeting(): void; // 必須在派生類中實作
}
class AccountingDepartment extends Department {
    constructor() {
        super('Accounting and Auditing'); // 在派生類的建構式中必須呼叫 super()
    }
    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }
    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}
let department: Department; // 允許創建一個對抽象型別的參考
department = new Department(); // 錯誤: 不能創建一個抽象類的實體
department = new AccountingDepartment(); // 允許對一個抽象子類進行實體化和賦值
department.printName();
department.printMeeting();
department.generateReports(); // 錯誤: 方法在宣告的抽象類中不存在

2.6類當做介面使用

class Point {
    x: number;
    y: number;
}
interface Point3d extends Point {
    z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};

4.泛型(generic)

function identity1<T>(arg: T): T {
    return arg;
}

1.泛型型別:與非泛型函式的型別沒什么不同,只是有一個型別引數在最前面,像函式宣告一樣:

function identity<T>(arg: T): T {
    return arg;
}
let myIdentity: <T>(arg: T) => T = identity; // or
let myIdentity1: {<T>(arg: T): T} = identity;
// 這引導我們去寫第一個泛型介面了, 我們把上面例子里的物件字面量拿出來做為一個介面
interface GenericIdentityFn {
    <T>(arg: T): T;
}
function identity<T>(arg: T): T {
    return arg;
}
let myIdentity: GenericIdentityFn = identity;

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; };
// GenericNumber類的使用是十分直觀的,并且你可能已經注意到了,沒有什么去限制它只能使用number型別, 也可以使用字串或其它更復雜的型別,
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));

5.列舉

1.數字列舉

enum Direction {
    Up = 1, // 使用初始值,遞增,否則默認從0開始
    Down,
    Left,
    Right
}

2.字串列舉

enum Direction2 {
    Up = "UP", // 每個字串列舉成員必須進行初始化
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

3.異構列舉(可以混合字串和數字成員)

例1:

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

例2:

enum E {
    Foo,
    Bar,
}
function f(x: E) {
    if (x !== E.Foo || x !== E.Bar) {
        //             ~~~~~~~~~~~
        // Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
    }
}

4.聯合列舉與列舉成員的型別

enum ShapeKind {
    Circle,
    Square,
}
interface Circle {
    kind: ShapeKind.Circle;
    radius: number;
}
interface Square {
    kind: ShapeKind.Square;
    sideLength: number;
}
let c11: Circle = {
    kind: ShapeKind.Square, // 正確的為ShapeKind.Circle
    //    ~~~~~~~~~~~~~~~~ Error!
    radius: 100,
}

5.運行時列舉

列舉是在運行時真正存在的物件

enum E {
    X, Y, Z
}
function f(obj: { X: number }) {
    console.log('X',obj.X);
    return obj.X;
}
// Works, since 'E' has a property named 'X' which is a number.
f(E);

6.反向映射

除了創建一個以屬性名做為物件成員的物件之外,數字列舉成員還具有了反向映射

enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
// 生成的代碼中,列舉型別被編譯成一個物件,它包含了正向映射( name -> value)和反向映射( value -> name), 參考列舉成員總會生成為對屬性訪問并且永遠也不會行內代碼,
// 要注意的是 不會為字串列舉成員生成反向映射,因為列舉成員不能具有數值名,所以數字列舉成員具有反射

7.常量(const)列舉

為了避免在額外生成的代碼上的開銷和額外的非直接的對列舉成員的訪問,我們可以使用 const列舉, 常量列舉通過在列舉上使用 const修飾符來定義,

常量列舉注意點:

1.不會生成反向映射

2.不能直接訪問值

const enum Order {
    A,
    B,
    C,
}

8.外部列舉

外部列舉用來描述已經存在的列舉型別的形狀,簡單理解就是方便用戶撰寫函式時的提示

declare enum Enum {
    A = 1,
    B,
    C = 2
}

外部列舉和非外部列舉之間有一個重要的區別,在正常的列舉里,沒有初始化方法的成員被當成常數成員, 對于非常數的外部列舉而言,沒有初始化方法時被當做需要經過計算的,

用來描述一個應該存在的列舉型別的,而不是已經存在的,它的值在編譯時不存在,只有等到運行時才知道,

6.模塊

TypeScript 1.5里術語名已經發生了變化, “內部模塊”現在稱做“命名空間”, “外部模塊”現在則簡稱為“模塊”,這是為了與 ECMAScript 2015里的術語保持一致,(也就是說 module X { 相當于現在推薦的寫法 namespace X {),

1.匯出

變數,函式,類,型別別名或介面都可以通過export匯出

匯出宣告

export interface StringValidator {
    isAcceptable(s: string): boolean;
}
export const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

匯出陳述句

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };

7.高級型別

1.交叉型別:多個型別合并為一個型別

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}
class Person1 {
    constructor(public name: string) { }
}
interface Loggable {
    log(): void;
}
class ConsoleLogger implements Loggable {
    log() {
        // ...
        return 11;
    }
}
var jim = extend(new Person1("Jim"), new ConsoleLogger());
var n1 = jim.name;
jim.log();

2.聯合型別

聯合型別表示一個值可以是幾種型別之一,用豎線( | )分隔每個型別,所以 number | string | boolean表示一個值可以是 number, string,或 boolean,

function padLeft(value: string, padding: string | number | boolean) {
    // ...
}
let indentedString = padLeft("Hello world", true); 
// 如果一個值是聯合型別,我們只能訪問此聯合型別的所有型別里共有的成員,
interface Bird {
    fly();
    layEggs();
}
interface Fish {
    swim();
    layEggs();
}
function getSmallPet(): Fish | Bird {
    // ...
}
let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors

3.型別保護

let pet = getSmallPet();
if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}

1.自定義型別保護

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}
// 'swim' 和 'fly' 呼叫都沒有問題了
if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}

2.typeof型別保護

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

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;
    }
}
function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("  ");
}
// 型別為SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
    padder; // 型別細化為'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // 型別細化為'StringPadder'
}

4.可以為null的型別

let s = "foo";
s = null; // 錯誤, 'null'不能賦值給'string'
let sn: string | null = "bar";
sn = null; // 可以
sn = undefined; // error, 'undefined'不能賦值給'string | null'

5.可選引數和可選屬性

使用了 --strictNullChecks,可選引數會被自動地加上 | undefined,

function f(x: number, y?: number) {
    return x + (y || 0);
}
f(1, 2);
f(1);
f(1, undefined);
f(1, null); // error, 'null' is not assignable to 'number | undefined'
// 可選屬性也會有同樣的處理:
class C {
    a: number;
    b?: number;
}
let c = new C();
c.a = 12;
c.a = undefined; // error, 'undefined' is not assignable to 'number'
c.b = 13;
c.b = undefined; // ok
c.b = null; // error, 'null' is not assignable to 'number | undefined'

6.型別斷言

可以為null的型別是通過聯合型別實作,那么你需要使用型別保護來去除 null,

如果編譯器不能夠去除 null或 undefined,你可以使用型別斷言手動去除, 語法是添加 !后綴: identifier!從 identifier的型別里去除了 null和 undefined:

function broken(name: string | null): string {
    function postfix(epithet: string) {
      return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
    }
    name = name || "Bob";
    return postfix("great");
}
function fixed(name: string | null): string {
    function postfix(epithet: string) {
      return name!.charAt(0) + '.  the ' + epithet; // ok
    }
    name = name || "Bob";
    return postfix("great");
}

本例使用了嵌套函式,因為編譯器無法去除嵌套函式的null(除非是立即呼叫的函式運算式), 因為它無法跟蹤所有對嵌套函式的呼叫,尤其是你將內層函式做為外層函式的回傳值, 如果無法知道函式在哪里被呼叫,就無法知道呼叫時 name的型別,

7.型別別名

型別別名會給一個型別起個新名字, 型別別名有時和介面很像,但是可以作用于原始值,聯合型別,元組以及其它任何你需要手寫的型別,

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    }
    else {
        return n();
    }
}
// 起別名不會新建一個型別 - 它創建了一個新名字來參考那個型別, 給原始型別起別名通常沒什么用,盡管可以做為檔案的一種形式使用,
// 同介面一樣,型別別名也可以是泛型 - 我們可以添加型別引數并且在別名宣告的右側傳入:
type Container<T> = { value: T };
// 我們也可以使用型別別名來在屬性里參考自己:
type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}
// 與交叉型別一起使用,我們可以創建出一些十分稀奇古怪的型別,
type LinkedList<T> = T & { next: LinkedList<T> };
interface Person {
    name: string;
}
var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
// 然而,型別別名不能出現在宣告右側的任何地方,
type Yikes = Array<Yikes>; // error

8.介面 & 型別別名

介面創建了一個新的名字,可以在其它任何地方使用,型別別名并不創建新名字—比如,錯誤資訊就不會使用別名, 在下面的示例代碼里,在編譯器中將滑鼠懸停在 interfaced上,顯示它回傳的是 Interface,但懸停在 aliased上時,顯示的卻是物件字面量型別,

type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

型別別名不能被 extends和 implements(自己也不能 extends和 implements其它型別), 因為軟體中的物件應該對于擴展是開放的,但是對于修改是封閉的,你應該盡量去使用介面代替型別別名,

如果你無法通過介面來描述一個型別并且需要使用聯合型別或元組型別,這時通常會使用型別別名,

9.字串字面量型別

字串字面量型別允許你指定字串必須的固定值,

type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
    animate(dx: number, dy: number, easing: Easing) {
        if (easing === "ease-in") {
            // ...
        }
        else if (easing === "ease-out") {
        }
        else if (easing === "ease-in-out") {
        }
        else {
            // error! should not pass null or undefined.
        }
    }
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
// 字串字面量型別還可以用于區分函式多載:
function createElement(tagName: "img"): HTMLImageElement;
function createElement(tagName: "input"): HTMLInputElement;
function createElement(tagName: string): Element { }

10.數字字面量型別

function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
    // ...
    return 1;
}
function foo(x: number) {
    if (x !== 1 || x !== 2) {
        //         ~~~~~~~
        // Operator '!==' cannot be applied to types '1' and '2'.
    }
}

11.可辨識聯合

你可以合并單例型別、聯合型別、型別保護和型別別名來創建一個叫做【可辨識聯合的高級模式】,它也稱做【標簽聯合】或【代數資料型別】,可辨識聯合在函式式編程很有用處,一些語言會自動地為你辨識聯合;而TypeScript則基于已有的JavaScript模式,它具有3個要素:

  1. 具有普通的單例型別屬性 — 可辨識的特征,
  2. 一個型別別名包含了那些型別的聯合 — 聯合,
  3. 此屬性上的型別保護,
interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}

首先我們宣告了將要聯合的介面,每個介面都有kind屬性但有不同的字串字面量型別,kind屬性稱做可辨識的特征或標簽,其它的屬性則特定于各個介面,注意,目前各個介面間是沒有聯系的,下面我們把它們聯合到一起:

type Shape = Square | Rectangle | Circle;
// 現在我們使用可辨識聯合:
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

12.完整性約束

當沒有涵蓋所有可辨識聯合的變化時,我們想讓編譯器可以通知我們, 比如,如果我們添加了 Triangle到 Shape,我們同時還需要更新 area:

type Shape = Square | Rectangle | Circle | Triangle;
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
    // should error here - we didn't handle case "triangle"
}

有兩種方式可以實作,

1.啟用 --strictNullChecks并且指定一個回傳值型別:

function area(s: Shape): number { // error: returns number | undefined
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

因為 switch沒有包涵所有情況,所以TypeScript認為這個函式有時候會回傳 undefined, 如果你明確地指定了回傳值型別為 number,那么你會看到一個錯誤,因為實際上回傳值的型別為 number | undefined, 然而,這種方法存在些微妙之處且 --strictNullChecks對舊代碼支持不好,

2.使用 never型別,編譯器用它來進行完整性檢查

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
        default: return assertNever(s); // error here if there are missing cases
    }
}

這里, assertNever檢查 s是否為 never型別—即為除去所有可能情況后剩下的型別, 如果你忘記了某個case,那么 s將具有一個真實的型別并且你會得到一個錯誤, 這種方式需要你定義一個額外的函式,但是在你忘記某個case的時候也更加明顯,

13.多型的this型別

多型的this型別表示的是某個包含類或介面的子型別,這被稱做F-bounded多型性,它能很容易的表現連貫介面間的繼承,比如,在計算器的例子里,在每個操作之后都回傳this型別:

class BasicCalculator {
    public constructor(protected value: number = 0) { }
    public currentValue(): number {
        return this.value;
    }
    public add(operand: number): this {
        this.value += operand;
        return this;
    }
    public multiply(operand: number): this {
        this.value *= operand;
        return this;
    }
    // ... other operations go here ...
}
let v = new BasicCalculator(2)
            .multiply(5)
            .add(1)
            .currentValue();

由于這個類使用了 this型別,你可以繼承它,新的類可以直接使用之前的方法,不需要做任何的改變,

class ScientificCalculator extends BasicCalculator {
    public constructor(value = 0) {
        super(value);
    }
    public sin() {
        this.value = https://www.cnblogs.com/abc-x/p/Math.sin(this.value);
        return this;
    }
    // ... other operations go here ...
}
let v = new ScientificCalculator(2)
        .multiply(5)
        .sin()
        .add(1)
        .currentValue();

如果沒有this型別,ScientificCalculator就不能夠在繼承BasicCalculator的同時還保持介面的連貫性,multiply將會回傳BasicCalculator,它并沒有sin方法,然而,使用this型別,multiply會回傳this,在這里就是ScientificCalculator,

14.索引型別

使用索引型別,編譯器就能夠檢查使用了動態屬性名的代碼,例如,一個常見的JavaScript模式是從物件中選取屬性的子集,

function pluck(o, names) {
    return names.map(n => o[n]);
}

下面是如何在TypeScript里使用此函式,通過 索引型別查詢和 索引訪問運算子:

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}
interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid',
    age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]

編譯器會檢查 name是否真的是 Person的一個屬性, 本例還引入了幾個新的型別運算子, 首先是 keyof T, 索引型別查詢運算子, 對于任何型別 T, keyof T的結果為 T上已知的公共屬性名的聯合, 例如:

let personProps: keyof Person; // 'name' | 'age'
// keyof Person是完全可以與 'name' | 'age'互相替換的, 不同的是如果你添加了其它的屬性到 Person,例如 address: string,那么 keyof Person會自動變為 'name' | 'age' | 'address', 
你可以在像 pluck函式這類背景關系里使用 keyof,因為在使用之前你并不清楚可能出現的屬性名, 但編譯器會檢查你是否傳入了正確的屬性名給 pluck:
pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age' // 第二個運算子是 T[K], 索引訪問運算子, 在這里,型別語法反映了運算式語法, 這意味著 person['name']具有型別 Person['name'] — 在我們的例子里則為 string型別,
然而,就像索引型別查詢一樣,你可以在普通的背景關系里使用 T[K],這正是它的強大所在, 你只要確保型別變數 K extends keyof T就可以了, 例如下面 getProperty函式的例子:
function getProperty<T, K extends keyof T>(o: T, name: K): T[K] { return o[name]; // o[name] is of type T[K] } // getProperty里的 o: T和 name: K,意味著 o[name]: T[K], 當你回傳 T[K]的結果,編譯器會實體化鍵的真實型別,因此 getProperty的回傳值型別會隨著你需要的屬性改變, let name: string = getProperty(person, 'name'); let age: number = getProperty(person, 'age'); let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'

15.索引型別和字串索引簽名

keyof和 T[K]與字串索引簽名進行互動, 如果你有一個帶有字串索引簽名的型別,那么 keyof T會是 string, 并且 T[string]為索引簽名的型別:

interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

16.映射型別

映射型別指從舊型別中創建新型別

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}
// 像下面這樣使用:
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;

最簡單的映射型別和它的組成部分:

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

它的語法與索引簽名的語法型別,內部使用了 for .. in, 具有三個部分:

  1. 型別變數 K,它會依次系結到每個屬性,
  2. 字串字面量聯合的 Keys,它包含了要迭代的屬性名的集合,
  3. 屬性的結果型別,

在個簡單的例子里, Keys是硬編碼的的屬性名串列并且屬性型別永遠是 boolean,因此這個映射型別等同于:

type Flags = {
    option1: boolean;
    option2: boolean;
}

在真正的應用里,可能不同于上面的 Readonly或 Partial, 它們會基于一些已存在的型別,且按照一定的方式轉換欄位, 這就是 keyof和索引訪問型別要做的事情:

type NullablePerson = { [P in keyof Person]: Person[P] | null }
type PartialPerson = { [P in keyof Person]?: Person[P] }
// 但它更有用的地方是可以有一些通用版本,
type Nullable<T> = { [P in keyof T]: T[P] | null }
type Partial<T> = { [P in keyof T]?: T[P] }

在這些例子里,屬性串列是 keyof T且結果型別是 T[P]的變體, 這是使用通用映射型別的一個好模版, 因為這類轉換是 同態的,映射只作用于 T的屬性而沒有其它的, 編譯器知道在添加任何新屬性之前可以拷貝所有存在的屬性修飾符, 例如,假設 Person.name是只讀的,那么 Partial<Person>.name也將是只讀的且為可選的,

下面是另一個例子, T[P]被包裝在 Proxy<T>類里:

type Proxy<T> = {
    get(): T;
    set(value: T): void;
}
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
}
function proxify<T>(o: T): Proxify<T> {
   // ... wrap proxies ...
}
let proxyProps = proxify(props);

注意 Readonly<T>和 Partial<T>用處不小,因此它們與 Pick和 Record一同被包含進了TypeScript的標準庫里:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}
type Record<K extends string, T> = {
    [P in K]: T;
}
// Readonly, Partial和 Pick是同態的,但 Record不是, 因為 Record并不需要輸入型別來拷貝屬性,所以它不屬于同態:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
// 非同態型別本質上會創建新的屬性,因此它們不會從它處拷貝屬性修飾符,
// 由映射型別進行推斷
// 現在你了解了如何包裝一個型別的屬性,那么接下來就是如何拆包, 其實這也非常容易:
function unproxify<T>(t: Proxify<T>): T {
    let result = {} as T;
    for (const k in t) {
        result[k] = t[k].get();
    }
    return result;
}
let originalProps = unproxify(proxyProps);

注意這個拆包推斷只適用于同態的映射型別, 如果映射型別不是同態的,那么需要給拆包函式一個明確的型別引數,

預定義的有條件型別

  • TypeScript 2.8在lib.d.ts里增加了一些預定義的有條件型別:
  • Exclude<T, U> -- 從T中剔除可以賦值給U的型別,
  • Extract<T, U> -- 提取T中可以賦值給U的型別,
  • NonNullable<T> -- 從T中剔除null和undefined,
  • ReturnType<T> -- 獲取函式回傳值型別,
  • InstanceType<T> -- 獲取建構式型別的實體型別,

例如:

type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "a" | "c"
type T02 = Exclude<string | number | (() => void), Function>;  // string | number
type T03 = Extract<string | number | (() => void), Function>;  // () => void
type T04 = NonNullable<string | number | undefined>;  // string | number
type T05 = NonNullable<(() => string) | string[] | null | undefined>;  // (() => string) | string[]
function f1(s: string) {
    return { a: 1, b: s };
}
class C {
    x = 0;
    y = 0;
}
type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]
type T14 = ReturnType<typeof f1>;  // { a: number, b: string }
type T15 = ReturnType<any>;  // any
type T16 = ReturnType<never>;  // any
type T17 = ReturnType<string>;  // Error
type T18 = ReturnType<Function>;  // Error
type T20 = InstanceType<typeof C>;  // C
type T21 = InstanceType<any>;  // any
type T22 = InstanceType<never>;  // any
type T23 = InstanceType<string>;  // Error
type T24 = InstanceType<Function>;  // Error

注意:Exclude型別是建議的Diff型別的一種實作,我們使用Exclude這個名字是為了避免破壞已經定義了Diff的代碼,并且我們感覺這個名字能更好地表達型別的語意,我們沒有增加Omit<T, K>型別,因為它可以很容易的用Pick<T, Exclude<keyof T, K>>來表示,

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

標籤:JavaScript

上一篇:如何使用Ajax實作簡單的檔案上傳

下一篇:跨域訪問方法介紹(5)--使用 window.postMessage 傳遞資料

標籤雲
其他(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