Flutter快學(xué)快用24講--02 Flutter Dart 語(yǔ)法:從 JavaScript 角度學(xué)習(xí) Dart
本課時(shí)我主要從 JavaScript 角度來(lái)講解如何學(xué)習(xí) Dart。
在學(xué)習(xí)本課時(shí)之前,你需要有一定的 JavaScript 基礎(chǔ),比如基礎(chǔ)數(shù)據(jù)類(lèi)型、函數(shù)、基礎(chǔ)運(yùn)算符、類(lèi)、異步原理和文件庫(kù)引入等,這也是 JavaScript 的核心知識(shí)點(diǎn)。接下來(lái)將通過(guò)對(duì)比與 JavaScript 的差異點(diǎn)來(lái)學(xué)習(xí) Dart 語(yǔ)言。
基礎(chǔ)數(shù)據(jù)類(lèi)型
與 JavaScript 相比較,我們整體上看一下圖 1 兩種語(yǔ)言的對(duì)比情況,相似的部分這里就不介紹了,比如 Number 和 String,其使用方式基本一致。下面主要基于兩者的差異點(diǎn)逐一講解,避免混淆或錯(cuò)誤使用。

圖 1 Dart 與 JavaScript 基礎(chǔ)數(shù)據(jù)類(lèi)型對(duì)比
Symbol 的區(qū)別
在 JavaScript 中,Symbol 是將基礎(chǔ)數(shù)據(jù)類(lèi)型轉(zhuǎn)換為唯一標(biāo)識(shí)符,核心應(yīng)用是可以將復(fù)雜引用數(shù)據(jù)類(lèi)型轉(zhuǎn)換為對(duì)象數(shù)據(jù)類(lèi)型的鍵名。
在 Dart 中,Symbol 是不透明的動(dòng)態(tài)字符串名稱(chēng),用于反映庫(kù)中的元數(shù)據(jù)。用 Symbol 可以獲得或引用類(lèi)的一個(gè)鏡像,概念比較復(fù)雜,但其實(shí)和 JavaScript 的用法基本上是一致的。例如,下面代碼首先 new 了一個(gè) test 為 Map 數(shù)據(jù)類(lèi)型,設(shè)置一個(gè)屬性 #t(Symbol 類(lèi)型),然后分別打印 test、test 的 #t、test 的 Symbol("t") 和 #t。
運(yùn)行代碼結(jié)果如下:
其中,test 包含了一個(gè)有 Symbol 為對(duì)象的 Key,value 為 symbol test 字符串的對(duì)象。test 的 #t 與 Symbol("t") 打印結(jié)果一致,#t 則與 Symbol("t') 是同一形式。
在上面的代碼示例中,兩者的核心在使用上基本是一致的,只是在理解方面相對(duì)不一樣。 Symbol 在 Dart 中是一種反射概念,而在 JavaScript 中則是創(chuàng)建唯一標(biāo)識(shí)的概念。
Undefined 和 Null
由于 Dart 是靜態(tài)腳本語(yǔ)言,因此在 Dart 中如果沒(méi)有定義一個(gè)變量是無(wú)法通過(guò)編譯的;而 JavaScript 是動(dòng)態(tài)腳本語(yǔ)言,因此存在腳本在運(yùn)行期間未定義的情況。所以這一點(diǎn)的不同決定了 Dart 在 Undefined 類(lèi)型上與 JavaScript 的差異。
null 在 Dart 中是的確存在的,官網(wǎng)上是這樣解釋的,null 是弱類(lèi)型 object 的子類(lèi)型,并非基礎(chǔ)數(shù)據(jù)類(lèi)型。所有數(shù)據(jù)類(lèi)型,如果被初始化后沒(méi)有賦值的話(huà)都將會(huì)被賦值 null 類(lèi)型。
下面的代碼,首先定義了一個(gè)弱類(lèi)型 number,其次定義了 int 類(lèi)型的 num2,number 類(lèi)型的 num1 以及 double 類(lèi)型的 num3 ,最后我們打印出這些只定義了未被賦值的值。
可以看到運(yùn)行結(jié)果如下:
從運(yùn)行結(jié)果我們可以看到,代碼中聲明了變量,但未賦值的變量在運(yùn)行時(shí)都會(huì)被賦值為 null,這就是 Dart 中 null 類(lèi)型存在的目的。
Map 和 List
Map 和 List 與 JavaScript 中的 Array 和 Map 基本一致,但在 JavaScript 中不是基本數(shù)據(jù)類(lèi)型,都屬于引用數(shù)據(jù)類(lèi)型。因此也就是分類(lèi)不同,但在用法和類(lèi)型上基本沒(méi)有太大差異。
弱類(lèi)型(var、object 和 dynamic)
相對(duì) JavaScript 而言,Dart 也存在弱類(lèi)型(可以使用 var、object 和 dynamic 來(lái)聲明),不過(guò)在這方面為了避免弱類(lèi)型導(dǎo)致的客戶(hù)端(App)Crash 的異常,Dart 還是對(duì)弱類(lèi)型加強(qiáng)了校驗(yàn)。
var 數(shù)據(jù)類(lèi)型聲明,第一次賦值時(shí),將其數(shù)據(jù)類(lèi)型綁定。下面代碼使用 var 聲明了一個(gè)弱類(lèi)型 t,并賦值 String 類(lèi)型 123,而接下來(lái)又對(duì) t 進(jìn)行其他類(lèi)型的賦值。
這樣的代碼在 Dart 編譯前就會(huì)報(bào)錯(cuò),因?yàn)?t 在一次 var 賦值時(shí)就已經(jīng)被綁定為 String 類(lèi)型了,再進(jìn)行賦值 Number 類(lèi)型時(shí)就會(huì)報(bào)錯(cuò)。
object 可以進(jìn)行任何賦值,沒(méi)有約束,這一點(diǎn)類(lèi)似 JavaScript 中的 var 關(guān)鍵詞賦值。在編譯期,object 會(huì)對(duì)數(shù)據(jù)調(diào)用做一定的判斷,并且報(bào)錯(cuò)。例如,聲明時(shí)為 String 類(lèi)型,但是在調(diào)用 length 時(shí),編譯期就會(huì)報(bào)錯(cuò)。如果數(shù)據(jù)來(lái)自接口層,則很容易導(dǎo)致運(yùn)行時(shí)報(bào)錯(cuò)。因此這個(gè)要盡量減少使用,避免運(yùn)行時(shí)報(bào)錯(cuò)導(dǎo)致客戶(hù)端(App)Crash 的異常。
dynamic 也是動(dòng)態(tài)的數(shù)據(jù)類(lèi)型,但如果數(shù)據(jù)類(lèi)型調(diào)用異常,則只會(huì)在運(yùn)行時(shí)報(bào)錯(cuò),這點(diǎn)是非常危險(xiǎn)的,因此在使用 dynamic 時(shí)要非常慎重。
基礎(chǔ)運(yùn)算符
兩種語(yǔ)言的基礎(chǔ)運(yùn)算符基本都一致。由于 Dart 是強(qiáng)數(shù)據(jù)類(lèi)型,因此在 Dart 中沒(méi)有 “=== ”的運(yùn)算符。在 Dart 中有一些類(lèi)型測(cè)試運(yùn)算符,與 JavaScript 中的類(lèi)型轉(zhuǎn)換和 typeof 有點(diǎn)相似。
這里也介紹一些 Dart 中比較簡(jiǎn)潔的寫(xiě)法:
?? 運(yùn)算符,比如,t??'test' 是 t!= null ? t : 'test' 的縮寫(xiě);
級(jí)聯(lián)操作,允許對(duì)同一對(duì)象或者同一函數(shù)進(jìn)行一系列操作,例如下面代碼的 testObj 對(duì)象中有三個(gè)方法 add()、delete() 和 show(),應(yīng)用級(jí)聯(lián)操作可以依次進(jìn)行調(diào)用。
函數(shù)
從我的理解來(lái)說(shuō),兩者區(qū)別不大。箭頭函數(shù)、函數(shù)閉包、匿名函數(shù)、高階函數(shù)、參數(shù)可選等基本上都一樣。在 Dart 中由于是強(qiáng)類(lèi)型,因此在聲明函數(shù)的時(shí)候可以增加一個(gè)返回類(lèi)型,這點(diǎn)在 TypeScript 中的用法是一致的,對(duì)于前端開(kāi)發(fā)人員來(lái)說(shuō),沒(méi)有太多的差異點(diǎn)。
類(lèi)
類(lèi)的概念在各種語(yǔ)言上大部分都是一致的,但在用法上可能存在差異,這里著重介紹一下 Dart 比較特殊的一些用法。
命名構(gòu)造函數(shù)
Dart 支持一個(gè)函數(shù)有多個(gè)構(gòu)造函數(shù),并且在實(shí)例化的時(shí)候可以選擇不同的構(gòu)造函數(shù)。
下面的代碼聲明了一個(gè) Dog 類(lèi),類(lèi)中有一個(gè) color 變量屬性和兩個(gè)構(gòu)造函數(shù)。red 構(gòu)造函數(shù)設(shè)置 Dog 類(lèi)的 color 屬性為 red,black 構(gòu)造函數(shù)設(shè)置 Dog 類(lèi)的 color 屬性為 black。最后在 main 函數(shù)中分別用兩個(gè)構(gòu)造函數(shù)創(chuàng)建兩個(gè)實(shí)例,并分別打印實(shí)例的 color 屬性。
運(yùn)行代碼后輸出了兩種顏色,即 red 和 black。就代碼而言,我們可以應(yīng)用同一個(gè)類(lèi)不同的構(gòu)造函數(shù)實(shí)現(xiàn)類(lèi)不同場(chǎng)景下的實(shí)例化。
訪(fǎng)問(wèn)控制
默認(rèn)情況下都是 public,如果需要設(shè)置為私有屬性,則在方法或者屬性前使用 “_”。
抽象類(lèi)和泛型類(lèi)
抽象類(lèi)和其他語(yǔ)言的抽象類(lèi)概念一樣,這里在 JavaScript 中沒(méi)有這種概念,因此這里稍微提及一下,主要是實(shí)現(xiàn)一個(gè)類(lèi)被用于其他子類(lèi)繼承,抽象類(lèi)是無(wú)法實(shí)例化的。
下面的代碼使用關(guān)鍵詞 abstract 聲明了一個(gè)有攻擊性的武器抽象類(lèi),包含一個(gè)攻擊函數(shù)和一個(gè)傷害力獲取函數(shù),Gun 和 BowAndArrow 都是繼承抽象類(lèi),并需要實(shí)現(xiàn)抽象類(lèi)中的方法。
泛型類(lèi),主要在不確定返回?cái)?shù)據(jù)結(jié)構(gòu)時(shí)使用,這點(diǎn)與 TypeScript 中的泛型概念一樣。
在下面的代碼中,我們不確定數(shù)組中存儲(chǔ)的類(lèi)型是 int 還是 string,又或者是 bool,這時(shí)候可以使用泛型 來(lái)表示。在使用泛型類(lèi)的時(shí)候可以將設(shè)定為自己需要的類(lèi)型,比如下面的 string 調(diào)用和 int 調(diào)用。
庫(kù)與調(diào)用
Dart 庫(kù)管理
Dart 和 JavaScript 一樣,有一個(gè)庫(kù)管理資源( pub.dev)。你可以在這里搜索找到你想要的一些庫(kù),接下來(lái)只要在 Dart 的配置文件 pubspec.yaml 中增加該庫(kù)即可。這點(diǎn)類(lèi)似于在 JavaScript 的 package.json 中增加聲明一樣,同樣也有 dependencies 和 dev_dependencies。
增加類(lèi)似的數(shù)據(jù)配置,如下代碼:
開(kāi)發(fā) Dart 庫(kù)
Dart 也支持開(kāi)發(fā)者自己開(kāi)發(fā)一些庫(kù),并且發(fā)布到 pub.dev 上,這點(diǎn)基本上和 npm 管理一致,這里我只介紹 pub.dev 庫(kù)的基本格式。
對(duì)于前端開(kāi)發(fā)人員來(lái)說(shuō),這個(gè)結(jié)構(gòu)和我們所看到的 npm 模塊很相似,pubspec 和 package 很相似,核心是 lib 中的庫(kù)名對(duì)應(yīng)的庫(kù)文件 .dart,該文件是一個(gè) dart 類(lèi)。類(lèi)的概念上面已經(jīng)介紹過(guò)了,將私有方法使用 "_" 保護(hù),其他就可以被引用該庫(kù)的模塊調(diào)用,如果是自身庫(kù)的一些實(shí)現(xiàn)邏輯,可以放在 src 中。
開(kāi)發(fā)完成該庫(kù)以后,如果需要發(fā)布到 pub.dev,則可以參照 官網(wǎng)的說(shuō)明,按步驟進(jìn)行即可。
Dart 調(diào)用庫(kù)
這里引入庫(kù)的方式也與 ES6 的 import 語(yǔ)法很相似。先看看下面的一個(gè)例子,其目的是引入 pages 下的 homepage.dart 模塊。
在上面的例子中,import 為關(guān)鍵詞,package 為協(xié)議,可以使用 http 的方式,不過(guò)最好使用本地 package 方式,避免性能受影響。接下來(lái)的 startup_namer 為庫(kù)名或者說(shuō)是該項(xiàng)目名,pages 為 lib 下的一個(gè)文件夾,homepage.dart 則為具體需要引入的庫(kù)文件名。
當(dāng)然這里也可以使用相對(duì)路徑的方式,不過(guò)建議使用 package 的方式,以保持整個(gè)項(xiàng)目代碼的一致性,因?yàn)閷?duì)于第三方模塊則必須使用 package 的方式。
總結(jié)
本課時(shí)首先介紹了 Dart 基礎(chǔ)數(shù)據(jù)類(lèi)型、基礎(chǔ)運(yùn)算符、類(lèi)以及庫(kù)與調(diào)用。然后通過(guò)對(duì)比 JavaScript 的一些特殊差異性,來(lái)加深前端開(kāi)發(fā)人員對(duì) Dart 語(yǔ)言編程的理解。相信你通過(guò)本課時(shí)的學(xué)習(xí),可以掌握 Dart 的編程,并且能夠?qū)懸恍?Dart 的第三方庫(kù)。
下一課時(shí),我將介紹 Dart 的事件循環(huán)機(jī)制,掌握了其核心運(yùn)行機(jī)制原理,才能編寫(xiě)出更高效、更有質(zhì)量的代碼。
點(diǎn)擊這里下載本課時(shí)源碼,F(xiàn)lutter 專(zhuān)欄,源碼地址: https://github.com/love-flutter/flutter-column