最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊

使用 Typescript 的一些注意事項(xiàng)

2021-03-23 18:35 作者:慕課網(wǎng)官方賬號  | 我要投稿

背景

我上學(xué)時(shí)學(xué)過 java 和 C# ,畢業(yè)后又做了兩年 C# 全棧開發(fā),對于靜態(tài)類型語言是有一定經(jīng)驗(yàn)的。ts 之所以能夠慢慢取代 js ,也是因?yàn)樗庆o態(tài)類型語言。

但 ts 和 java 是不一樣的,本質(zhì)是因?yàn)樗鳛橐粋€(gè)靜態(tài)類型語言,要編譯成弱類型語言 js 來執(zhí)行。所以,ts 只管得了編譯時(shí),卻管不了運(yùn)行時(shí)。下文的很多內(nèi)容,都是這個(gè)特點(diǎn)的具體表現(xiàn)。

【個(gè)人提醒】我感覺 ts 為了能讓自己更適應(yīng) js 的轉(zhuǎn)型,做了很多非常繁瑣(或者叫靈活)的設(shè)計(jì),我沒有詳細(xì)總結(jié),但這種感覺很強(qiáng)烈。所以,如果你覺得 ts 有些地方過于繁瑣時(shí),也不要擔(dān)心,這可能不是你的問題,而是它的問題。

任何美好的東西,都是應(yīng)該簡單的、明確的。

易混亂的類型

如果問“ts 的變量有多少種類型”,你能否回答全面?ts 比 js 類型多一些。

never?vs?void

只需要記住一個(gè)特點(diǎn):返回?never?的函數(shù),都必須存在無法到達(dá)的終點(diǎn),如死循環(huán)、拋出異常。

function fn1(): never { while(true) { /*...*/ }}function fn2(): never { throw new Error( /*...*/ )}

any?vs?unknown

  • any?任何類型,會(huì)忽略語法檢查

  • unknown?不可預(yù)知的類型,不會(huì)忽略語法檢查(這就是最大區(qū)別)

const bar: any = 10;any.substr(1); // OK - any 會(huì)忽略所有類型檢查const foo: unknown = 'string';foo.substr(1); // Error: 語法檢查不通過報(bào)錯(cuò)// (foo as string).substr(1) // OK// if (typeof foo === 'string') { foo.substr(1) } // OK

一些“欺騙”編譯器語法檢查的行為

就如同你告訴編譯器:“按我寫的來,不要管太多,出了事兒我負(fù)責(zé)!”

編譯器不給你添麻煩了,不進(jìn)行語法檢查了,但你一定要考慮好后果。所以,以下內(nèi)容請慎用,不要無腦使用。

@ts-ignore

增加?@ts-ignore?的注釋,會(huì)忽略下一行的語法檢查。

const num1: number = 100num1.substr() // Error 語法檢查錯(cuò)誤const num2: number = 200// @ts-ignorenum2.substr() // Ok 語法檢查通過

any

如果 ts 是西游記,any?就是孫悟空,自由、無約束。了解西游記大部分是從孫悟空開始,了解 ts 可能也是從?any?開始用。

但西游記最后,孫悟空變成了佛。你的?any?也應(yīng)該變成 interface 或者 type 。

類型斷言?as

文章一開始說過,ts 只管編譯時(shí),不管運(yùn)行時(shí)。as?就是典型的例子,你用?as?告訴編譯器類型,編譯器就聽你的。但運(yùn)行時(shí),后果自負(fù)。

function fn(a: string | null): void { ? ?const length = (a as string).length ? ?console.log(length)}fn('abc') // Ok// fn(null) // Error js 運(yùn)行報(bào)錯(cuò)

非空斷言操作符?!

!?用于排除?null?undefined?,即告訴編譯器:xx 變量肯定不是?null?或?undefined?,你放心吧~

同理,運(yùn)行時(shí)有可能出錯(cuò)。

// 例子 1function fn(a: string | null | undefined) { ? ?let s: string = '' ? ?s = a // Error 語法檢查失敗 ? ?s = a! // OK —— 【注意】如果 a 真的是 null 或者 undefined ,那么 s 也會(huì)是 null 或者 undefined ,可能會(huì)帶來 bug ?。?!}// fn(null)// 例子 2type NumGenerator = () => number;function myFunc(numGenerator: NumGenerator | undefined) { ?const num1 = numGenerator(); // Error 語法檢查失敗 ?const num2 = numGenerator!(); // OK}// myFunc(undefined) // 【注意】,如果真的傳入 undefined ,也會(huì)去執(zhí)行,當(dāng)然會(huì)執(zhí)行報(bào)錯(cuò)?。?!// 例子 3let a: numberconsole.log(a) // Error - Variable 'n' is used before being assigned.let b!: numberconsole.log(b) // OK - `!` 表示,你會(huì)給 b 一個(gè)賦值,不用編譯器關(guān)心

可選鏈??.

?.?遇到?null?或?undefined?就可以立即停止某些表達(dá)式的運(yùn)行,并返回?undefined?

【注意】這里只針對?null?和?undefined?,對于?0?false?''?等 falsely 變量是不起作用的。這一點(diǎn)和?&&?不一樣。

這個(gè)運(yùn)算符,看似是獲取一個(gè)屬性,其實(shí)它是有條件判斷的。即,它就是一個(gè)?? :?三元表達(dá)式的語法糖。既然它有判斷邏輯,那你考慮不到位,就有可能出錯(cuò)。

// 例子 1 - 獲取對象屬性interface IFoo { a: number }function fn(obj: IFoo | null | undefined): number | undefined { ? ?const a = obj?.a // ?. 可選鏈運(yùn)算符 ? ?// 第一,如果 a 是 IFoo 類型,則打印 100 ? ?// 第二,如果 a 是 null 或者 undefined ,則打印 undefined ? ?console.log('a', a) ? ?return a // 100 或者 undefined}fn({ a: 100 })// fn(null)// fn(undefined)// 例子 2 - 獲取數(shù)組元素function tryGetArrayElement<T>(arr?: T[], index: number = 0) { ?return arr?.[index];}// 編譯產(chǎn)出:// "use strict";// function tryGetArrayElement(arr, index = 0) {// ? ? return arr === null || arr === void 0 ? void 0 : arr[index];// }// 例子 3 - 用于函數(shù)調(diào)用type NumGenerator = () => number;function fn(numGenerator: NumGenerator | undefined | null) { ?const num = numGenerator?.(); ?console.log('num', num) // 如果不是函數(shù),則不調(diào)用,也不會(huì)報(bào)錯(cuò),返回 undefined}// fn(null)// fn(undefined)

【吐槽】對于這種語法糖,我還是比較反感的,我覺得自己寫幾行邏輯判斷會(huì)更好。它雖然簡潔,但是它會(huì)帶來閱讀理解上的負(fù)擔(dān),代碼簡潔不一定就可讀性好 —— 當(dāng)然了,如果大家都這么用,用久了,大家都熟悉了,可能也就沒有這個(gè)障礙了。

type 和 interface

關(guān)于兩者的區(qū)別,大家可以看看這篇文章?,本文主要說一下我的理解。

先說結(jié)論:我目前還是處于一種懵逼狀態(tài)。我感覺 type 和 insterface 有太多的灰色地帶,這就導(dǎo)致我們?nèi)粘J褂脮r(shí),大部分情況下用誰都可以。我搞不懂 ts 為何要這樣設(shè)計(jì)。

按照我前些年對 java 和 C# 的理解:(我不知道近幾年 java C# 有沒有相關(guān)的語法變化)

  • 如果自定義一個(gè)靜態(tài)的類型,僅有一些屬性,沒有方法,就用?type

  • 如果定義一種行為(行為肯定是需要方法的,僅屬性是不夠的),需要 class 實(shí)現(xiàn),就用?interface

但是查到的資料,以及查閱 ts 的類庫 lib.dom.d.ts 和 lib.es2015.d.ts 源碼,也都是用 interface 。我曾經(jīng)一度很困惑,見的多了,就慢慢習(xí)慣成自然了,但問題并沒有解決。

問題沒有解決,但事情還是要繼續(xù)做的,代碼也是要繼續(xù)寫的,所以我就一直跟隨大眾,盡量用 interface 。

private?和?#

兩者都表示私有屬性。背景不同:

  • private?是 ts 中一開始就有的語法,而且目前只有 ts 有,ES 規(guī)范沒有。

  • #?是 ES 目前的提案語法,然后被 ts 3.8 支持了。即,ts 和 ES 都支持?#?。

如果僅對于 ts 來說,用哪個(gè)都一樣。

但本文一開始提到過:ts 只關(guān)注編譯時(shí),不關(guān)注運(yùn)行時(shí)。所以,還得看看兩者的編譯結(jié)果。

private

private?編譯之后,就失去了私有的特點(diǎn)。即,如果你執(zhí)行?(new Person()).name?,雖然語法檢查不通過,但運(yùn)行時(shí)是可以成功的。

即,private?僅僅是 ts 的語法,編譯成 js 之后,就失效了。

// ts 源碼class Person { ? ?private name: string ? ?constructor() { ? ? ? ?this.name = 'zhangsan' ? ?}}/* 編譯結(jié)果如下 "use strict"; class Person { ? ?constructor() { ? ? ? ?this.name = 'zhangsan'; ? ?} } */

#

#?編譯之后,依然具有私有特點(diǎn),而且用?(new Person()).name?,在運(yùn)行時(shí)也是無法實(shí)現(xiàn)的。

即,#?是 ts 語法,但同時(shí)也是 ES 的提案語法,編譯之后也不能失效。

但是,編譯結(jié)果中,“私有”是通過?WeekMap?來實(shí)現(xiàn)的,所以要確保你的運(yùn)行時(shí)環(huán)境支持 ES6?。WeekMap?沒有完美的 Polyfill 方案,強(qiáng)行 Polyfill 可能會(huì)發(fā)生內(nèi)存泄漏。

// ts 源碼class Person { ? ?#name: string ? ?constructor() { ? ? ? ?this.#name = 'zhangsan' ? ?}}/* 編譯結(jié)果如下 "use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) { ? ?if (!privateMap.has(receiver)) { ? ? ? ?throw new TypeError("attempted to set private field on non-instance"); ? ?} ? ?privateMap.set(receiver, value); ? ?return value; }; var _name; class Person { ? ?constructor() { ? ? ? ?_name.set(this, void 0); ? ? ? ?__classPrivateFieldSet(this, _name, 'zhangsan'); ? ?} } _name = new WeakMap(); */

函數(shù)重載

java 中的函數(shù)重載

java 中的函數(shù)重載是非常好用,而且非常好理解的,傻瓜式的,一看就懂。

如下代碼,定義了四個(gè)名為?test?的函數(shù),參數(shù)不同。那就直接寫四個(gè)函數(shù)即可,調(diào)用時(shí)也直接調(diào)用,java 會(huì)自動(dòng)匹配。

public class Overloading { ? ?public int test(){ ? ? ? ?System.out.println("test1"); ? ? ? ?return 1; ? ?} ? ?public void test(int a){ ? ? ? ?System.out.println("test2"); ? ?} ? ? ?public String test(int a,String s){ ? ? ? ?System.out.println("test3"); ? ? ? ?return "returntest3"; ? ?} ? ? ?public String test(String s,int a){ ? ? ? ?System.out.println("test4"); ? ? ? ?return "returntest4"; ? ?} ? ? ?public static void main(String[] args){ ? ? ? ?Overloading o = new Overloading(); ? ? ? ?System.out.println(o.test()); ? ? ? ?o.test(1); ? ? ? ?System.out.println(o.test(1,"test3")); ? ? ? ?System.out.println(o.test("test4",1)); ? ?}}

ts 中的函數(shù)重載

ts 的函數(shù)重載,先把各個(gè)情況的函數(shù)頭寫出來,然后再寫一個(gè)統(tǒng)一的、兼容上述所有情況的函數(shù)頭。最后,函數(shù)體自行處理參數(shù)。

class Person { ? ?// 第一,各個(gè)情況的函數(shù)頭寫出來 ? ?test(): void ? ?test(a: number, b: number): number ? ?test(a: string, b: string): string ? ?// 第二,統(tǒng)一的、兼容上述所有情況的函數(shù)頭(有一個(gè)不兼容,就報(bào)錯(cuò)) ? ?test(a?: string | number, b?: string | number): void | string | number { ? ? ? ?// 第三,函數(shù)體自行處理參數(shù) ? ? ? ? ? ? ? ?if (typeof a === 'string' && typeof b === 'string') { ? ? ? ? ? ?return 'string params' ? ? ? ?} ? ? ? ?if (typeof a === 'number' && typeof b === 'number') { ? ? ? ? ? ?return 'number params' ? ? ? ?} ? ? ? ?console.log('no params') ? ?}}

這和 java 的語法比起來,簡直就是復(fù)雜 + 丑陋,完全違背設(shè)計(jì)原則。

但是,為何要這樣呢?最終還是因?yàn)?ts 只關(guān)注編譯時(shí),管不了運(yùn)行時(shí) —— 這是原罪。

試想,如果 ts 也設(shè)計(jì)像 java 一樣的重載寫法,那編譯出來的 js 代碼就會(huì)亂套的。因?yàn)?js 是弱類型的。

注意函數(shù)定義的順序

參數(shù)越精準(zhǔn)的,放在前面。

/* 錯(cuò)誤:any 類型不精準(zhǔn),應(yīng)該放在最后 */declare function fn(x: any): any;declare function fn(x: HTMLElement): number;declare function fn(x: HTMLDivElement): string;var myElem: HTMLDivElement;var x = fn(myElem); // x: any, wat?

不要為僅在末尾參數(shù)不同時(shí)寫不同的重載,應(yīng)該盡可能使用可選參數(shù)。

/* 錯(cuò)誤 */interface Example1 { ? ?diff(one: string): number; ? ?diff(one: string, two: string): number; ? ?diff(one: string, two: string, three: boolean): number;}/* OK */interface Example2 { ? ?diff(one: string, two?: string, three?: boolean): number;}

DOM 相關(guān)的類型

Vue 和 React 框架的普及,讓大部分業(yè)務(wù)開發(fā)者不用直接操作 DOM ,變成了框架工程師。但 Web 是基于 DOM 的,可以不用,但千萬不要忘記。

js 寫 DOM 操作非常簡單,不用關(guān)心類型,直接訪問屬性和方法即可。但用 ts 之后,就得關(guān)心 DOM 操作的相關(guān)類型。

不光我們使用 ts ,微軟在設(shè)計(jì) ts 時(shí),也需要定義 DOM 操作相關(guān)的類型,放在 ts 的類庫中,這樣 ts 才能被 web 場景所使用。這些都定義在?lib.dom.d.ts?中。補(bǔ):還有 ES 語法的內(nèi)置類庫,也在同目錄下。

PS:一門成熟可用的編程語言,最基本的要包括:語法 + 類庫 + 編譯器 + 運(yùn)行時(shí)(或者編譯器和運(yùn)行時(shí)統(tǒng)一為解釋器)。然后再說框架,工具,包管理器等這些外圍配置。

Node Element 等類型

這些都是現(xiàn)成的,W3C 早就定義好了的,我們直接回顧一下就可以。我覺得一張圖就可以很好的表達(dá),詳細(xì)的可以參考各自的 MDN 文檔。

事件參數(shù)類型

在使用 ts 之前,我并沒有特別關(guān)注事件參數(shù)類型(或者之前看過,后來不用,慢慢忘了),反正直接獲取屬性,拿來用就可以。

document.body.addEventListener('click', e1 => { ? ?// e1 的構(gòu)造函數(shù)是什么?})document.body.addEventListener('keyup', e2 => { ? ?// e2 的構(gòu)造函數(shù)是什么?})

于是我查了一下 MDN 的文檔,其實(shí)也很好理解,就是不同的事件,參數(shù)類型是不一樣的,當(dāng)然屬性、方法也就不一樣。下面列出我們常見的,所有的類型參考?MDN 這個(gè)文檔。

事件參數(shù)類型click dbclick mouseup mousedown mousemove mouseenter mouseleaveMouseEventkeyup keyrpess keydownKeyboardEventcompositionstart compositionupdate compositionend(輸入法)CompositionEventfocus blur focusin focusoutFocusEventdrag dropDragEventpaste cut copyClipboardEvent

他們的繼承關(guān)系如下圖。其中?UIEvent?表示的是用戶在 UI 觸發(fā)的一些事件。因?yàn)槭录粌H僅是用戶觸發(fā)的,還有 API 腳本觸發(fā)的,所以要單獨(dú)拿出一個(gè)?UIEvent?,作為區(qū)分。

總結(jié)

我感覺重點(diǎn)的就是那句話:ts 是一門靜態(tài)類型語言,但它要編譯成為 js 這個(gè)弱類型語言來執(zhí)行,所以它管得了編譯時(shí),卻管不了運(yùn)行時(shí)。這是很多問題的根本。

目前看來,前端社區(qū)會(huì)慢慢往 ts 轉(zhuǎn)型,所以能熟練使用 ts 已經(jīng)是一名前端人員必備的技能。希望本文能給大家?guī)硪稽c(diǎn)點(diǎn)幫助。

相關(guān)視頻傳送門:

https://www.bilibili.com/video/BV1sU4y1a7ri

https://www.bilibili.com/video/BV1WK4y1J7fs


作者:雙越
鏈接:https://www.imooc.com/article/315202
來源:慕課網(wǎng)
本文原創(chuàng)發(fā)布于慕課網(wǎng) ,轉(zhuǎn)載請注明出處,謝謝合作

使用 Typescript 的一些注意事項(xiàng)的評論 (共 條)

分享到微博請遵守國家法律
苍南县| 肇东市| 曲阳县| 东辽县| 汾西县| 朔州市| 台州市| 兴山县| 衢州市| 商河县| 蕲春县| 延寿县| 吴江市| 泰州市| 漠河县| 七台河市| 青川县| 盱眙县| 平原县| 高清| 衡阳市| 廊坊市| 石阡县| 长岭县| 聂荣县| 绥棱县| 洞头县| 绩溪县| 沁水县| 古交市| 祁门县| 武强县| 栖霞市| 梅河口市| 甘孜| 广丰县| 万全县| 兴化市| SHOW| 威信县| 永靖县|