天天看點

js 運算符 感歎号後置 是斷言

typescript變量名後面的感歎号是什麼意思?

ts的代碼如下:

let foo = () =>{
    let name = "alberto";
    return name!;
}
document.body.innerHTML = foo();
           

編譯成js後的代碼如下:

var foo = function () {
    var name = "alberto";
    return name;
};
document.body.innerHTML = foo();
           

不了解name變量後面的感歎号是什麼意思?

感歎号(非空斷言操作符) ,就你這個來說沒有什麼意義,因為name是常量,如果name可能為空,可以用這個,如果為空,會丢出斷言失敗。

Typescript中的as、問号與感歎号

1、as關鍵字表示斷言

在Typescript中,表示斷言有兩種方式。一種是擴号表示法:

let someValue: any = "this is a string";
let strLength: number = (someValue).length;
           
  • 1
  • 2

另一種使用as關鍵字:

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

2、問号(?)用于屬性定義

問号表示可選的屬性,一般用于屬性定義,如,用于接口時:

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig) {
    if (config.clor) {
        console.log(config);
    }
}
           
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

可選屬性的含義是:使用這個屬性時,要麼這個屬性名不存在,要麼必須符合屬性的類型定義

比如上述createSquare函數編譯時會報error錯誤:

error TS2551: Property 'clor' does not exist on type 'SquareConfig'.
           

如果修改createSquare,将config.color的值改為undefined,會怎樣?

interface SquareConfig {
    color?: string;
    width?: number;
}
function createSquare(config: SquareConfig) {
    config.color = undefined;
    if (config.color) {
        console.log(config);
    }
}
           
  • 10

這時并沒有編譯報錯!明明config.color定義的是string類型呀?

即便是添加–strictNullChecks進行編譯,也不會報錯。可見,可選屬性所定義的類型,并沒有被typescript嚴格地對待,預設并不檢查undefined。需要注意的是,将上述undefined改成null時,普通編譯也不報錯,–strictNullChecks編譯會報如下錯誤:

error TS2322: Type 'null' is not assignable to type 'string | undefined'
           

從這句報錯中,我們可以得出這樣的結論:可選屬性等價于一個union類型,union了undefined;不加–strictNullChecks編譯時,null可以指派給undfined類型。也就是說,SquareConfig的定義與下面的代碼等價:

interface SquareConfig {
    color: string|undefined;
    width: number|undefined;
}
           

下面比較一下可選屬性與正常屬性。再次修改createSquare,将color屬性修改為正常屬性。

interface SquareConfig {
    color: string;
    width?: number;
}
function createSquare(config: SquareConfig) {
    config.color = undefined;
    if (config.color) {
        console.log(config);
    }
}
           

以–strictNullChecks編譯,報錯了:

error TS2322: Type ‘undefined’ is not assignable to type ‘string’

這個比較也驗證了上述的結論。

問号(?)用于屬性讀取

問号用于屬性讀取,主要有兩個場景:一是讀取數組元素(如下面的node[i]),二是讀取不确定的類型如any,union,可選類型(如node[i].data)等。如下例,儲存為index.ts:

interface VNodeData {
    class?: string;
}
interface VNode {
    sel?: string;
    data?: VNodeData;
}
function test(node: VNode[]) {
    let i = 0;
    var b = node[i].data.class;
    if(b !== undefined) {
        console.log(1);
    }
}
           
  • 11
  • 12
  • 13
  • 14

用tsc --strictNullChecks index.ts,報錯:.

error TS2532: Object is possibly 'undefined'
           

下面将一一展示這一行代碼var b = node[i].data.class;修改改後的效果。

1、修改為var b = node[i]?.data.class;,然後編譯。報錯:

Object is possibly 'undefined'
           

2、修改為var b = node[i]?.data?.class;,然後編譯。編譯通過,檢視編譯後的對應代碼為:

function test(node) {
    var _a, _b;
    var i = 0;
    var b = (_b = (_a = node[i]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b["class"];
    // var b = node[i].data.class;//報錯
    if (b !== undefined) {
        console.log(1);
    }
}
           

var b = node[i]?表示,如果node[i]的值為null或者undefined,則b等于undefined,否則b=node[i]。

3、修改為var b = (node[i] as VNode).data?.class;,然後編譯。編譯通過,檢視編譯後的對應代碼為:

function test(node) {
    var _a;
    var i = 0;
    var b = (_a = node[i].data) === null || _a === void 0 ? void 0 : _a["class"];
    // var b = node[i]?.data?.class;
    // var b = node[i].data.class;//報錯
    if (b !== undefined) {
        console.log(1);
    }
}
           

此時,使用node[i]時,Typescript編譯器将不再對其判斷是否為null和undefined。即:var b = node[i] as VNode直接會被編成var b = node[i]。

4、修改為var b = node[i]!.data?.class,然後編譯。編譯通過,檢視編譯後的對應代碼為:

function test(node) {
    var _a;
    var i = 0;
    var b = (_a = node[i].data) === null || _a === void 0 ? void 0 : _a["class"];
    // var b = (node[i] as VNode).data?.class
    // var b = node[i]?.data?.class;
    // var b = node[i].data.class;//報錯
    if (b !== undefined) {
        console.log(1);
    }
}
           

可見,3和4的編譯後代碼完全一樣,!的作用此時與as是等價的。然而,!隻是用來判斷null和undefined;as則可用于變更(縮小或者放大都可以)類型檢測範圍,僅當as後面跟的類型是一個非空類型時,兩者才等價。如下例中,不能将as用法改為!。

interface Cat {
    action: string;
}
interface Dog {
    action: string;
}
type Animal = Cat | Dog;
let action:Animal = {} as Cat;
           

結論

1、as和!用于屬性的讀取,都可以縮小類型檢查範圍,都做判空用途時是等價的。隻是!具體用于告知編譯器此值不可能為空值(null和undefined),而as不限于此。

2、?可用于屬性的定義和讀取,讀取時告訴編譯器此值可能為空值(null和undefined),需要做判斷。

一、感歎号

1. 一個感歎号

一個感歎号(!)最好了解,也是我們經常使用的。!可以将變量轉為boolean值,所有的truthy和falsy都可以被轉化,比如null、undefined、數字0和空字元串取反都為true,其餘都為false。

!null = true
!undefined = true
!"" = true
!0 = true
!100 = false
!"asdss" = false
           

注意:truthy和falsy不是true和false

2. 兩個感歎号

兩個感歎号(!!),學習時比較少見到,通常用來判斷變量是不是truthy或者falsy,這麼說好像和前面一樣?不,一個感歎号隻是單個判斷比如隻對null或者隻對undefined,使用兩個!!可以對所有truthy和falsy判斷,比如:

let a; // null、undefined、''、0
    if (a !== null && typeof(a) !== "undefined" && 
        a !== undefined && a !== '' && a !== 0){
          //a為truthy時才執行,或者說a為真值時才執行
       }
           
let a;
	if(!!a){
	//a為truthy時才執行,或者說a為真值時才執行
	}
           

這兩種方法的功能一樣,但是明顯下面一種更加簡潔。

好像還有三個!!!的,但是并沒有什麼用處。似乎是将1和2都做了一遍,然後回到原點。

二、3個點(…)

三個點用于傳參時可以看作是Rest參數的逆運算,将一個數組轉為用逗号分隔的參數數組,就是說将參數數組展開了傳入函數中。

let a = [1,2,3]
function f(x1,x2,x3){
	...
}
f(...a) // 等價于 f(1,2,3)
           
Rest參數和arguments對象的差別:
  • rest參數隻包括那些沒有給出名稱的參數,arguments包含所有參數
  • arguments 對象不是真正的數組,而rest 參數是數組執行個體,可以直接應用sort, map, forEach, pop等方法
  • arguments 對象擁有一些自己額外的功能

當用于函數形參時,後面就不能有其他參數,如下将會報錯

function func(a, ...b, c) {
    // ...
}
// Rest parameter must be last formal parameter
           

使用三個點,在許多時候可以代替數組的apply方法

// ES6以前
Math.max.apply(null, [22, 2, 222])
// ES6
Math.max(...[22, 2, 222])
//  等價于
Math.max(22, 2, 222);           
js