看一段常見程式碼:
//例1
function foo(x) {
return x + 10
}
foo(`Hello!`)
//例2
function main(params){
//fn1函式獲取了一個資料
var object = fn1(params)
//fn2根據獲資料,產生一個結果
var result = fn2(object)
return result
}
複製程式碼
例2很明顯,這個過程非常的‘黑’,如果你想知道object包含什麼資料的話,可以:
- 列印一下 console.log(object)
- 檢視fn1的註釋,並且保佑它的註釋是正確,全面的
- 或結合1,2,然後仔細檢視fn1的原始碼,希望它不是很複雜
被上述步驟折磨完之後,終於能真正的寫點程式碼了,但是依舊得非常小心,因為這裡還有另一個函式:fn2
在修改程式碼的時候,得保證result這個結果沒有被影響,那麼如何保證呢?
很簡單,重複上面的步驟,搞清楚result包含的資料,在測試的時候確保其資料跟原先的相同。
…
動態型別一時爽,程式碼重構火葬場
知乎傳送門:為什麼說“動態型別一時爽,程式碼重構火葬場”
是時候徹底優化這個煩人的問題了
引入型別系統
其實問題的根源就是因為javascript太靈活了,在程式碼執行期間幾乎可以做任何的修改,
沒有東西可以在程式碼層面保證 某個變數,某個函式 跟預期的一致。
所以要加入型別系統來確保程式碼的可靠性,在後期維護的時候同樣能夠傳達出有效的資訊
Flow & TypeScript
Flow是個JavaScript的靜態型別檢查工具,由Facebook出品的開原始碼專案,問世只有兩三年,是個相當年輕的專案。簡單來說,它是對比TypeScript語言的解決方式。
會有這類解決方案,起因是JavaScript是一種弱(動態)資料型別的語言,弱(動態)資料型別代表在程式碼中,變數或常量會自動依照賦值變更資料型別,而且型別種類也很少,這是直譯式指令碼語言的常見特性,但有可能是優點也是很大的缺點。優點是容易學習與使用,缺點是像開發者經常會因為賦值或傳值的型別錯誤,造成不如預期的結果。有些時候在使用框架或函式庫時,如果沒有仔細看檔案,亦或是檔案寫得不清不楚,也容易造成誤用的情況。
這個缺點在應用規模化時,會顯得更加嚴重。我們在團隊開發協同時,一般都是通過統一的程式碼規範,來降低這個問題的發生,但JS語言本身
無法有效阻止這些問題。TypeScript這樣的強(靜態)型別的JavaScript超集語言就開始流行,用嚴格的角度,以JavaScript語言為基底,來重新打造另一套具有強(靜態)型別特性的語言,就如同Java或C#這些語言一樣,這也是為什麼TypeScript稱自己是企業級的開發JavaScript解決方案。
TypeScript存在的問題
TypeScript自然有它的市場,但它有一些明顯的問題:
- 首先是JavaScript開發者需要再進一步學習,內容不少
- 有一定陡峭的學習曲線
- 已經在使用的應用程式碼,需要整個改用TypeScript程式碼語法,才能發揮完整的功用。這對很多已經有內部程式碼庫的大型應用開發團隊而言,將會是個重大的決定,因為如果不往全面重構的路走,將無法發揮強(靜態)型別語言的最大效用eg:angular2
所以許多現行的開原始碼函式庫或框架,並不會直接使用TypeScript作為程式碼的語言,另一方面因為TypeScript並非是普及到一定程度的語言。
當然TypeScript也是個活躍的開原始碼專案,發展到現在也有一段時間,它的背後有微軟公司的支援,全新打造過的Angular2框架中(由Google主導),也採用了TypeScript作為基礎的開發語言。
Flow你的新選擇
現在,Flow提供了另一個新的選項,它是一種強(靜態)型別的輔助檢查工具
。Flow的功能是讓現有的JavaScript語法可以事先作型別的宣告(定義),在開發過程中進行自動檢查
,當然在最後編譯時,一樣可以用babel工具來移除這些標記。
相較於TypeScript是另外重新制定一套語言,最後再經過編譯為JavaScript程式碼來執行。Flow走的則是非強制與非侵入性的路線。
Flow的優點
輕
且易學易用
它的學習曲線沒有TypeScript來得高,雖然內容也很多,但半天學個大概,就可以漸進式地開始使用- Flow從頭到尾只是個
檢查工具
不是新的程式語言或超集語言,所以它可以與各種現有的JavaScript程式碼相容,如果你哪天不想用了,就去除掉標記就是回到原來的程式碼,沒什麼負擔
so
選擇flow.js工具而不選擇TypeScript強型別語言的原因顯而易見?
flow.js對工程的侵入性很小,無需大量的額外工作就能使用起來
從一個小例子演示
這種型別不符的情況在程式碼中非常容易發生,例如上面的例1:
function foo(x) {
return x + 10
}
foo(`Hello!`)
複製程式碼
x這個傳參,我們在函式宣告時希望它是個數字型別,但最後使用呼叫函式時則用了字串型別。最後的結果會是什麼嗎? “Hello!10″,這是因為加號(+)在JavaScript語言中,除了作為數字的加運算外,也可以當作字串的連線運算。想當然這並不是我們想要的結果。
聰明如你應該會想要用型別來當傳參的識別名,容易一眼看出傳參要的是什麼型別,像下面這樣:
function foo(number) {
return number + 10
}
複製程式碼
- 如果在複合型別的情況,例如這個傳參的型別可以是數字型別也可以是布林型別,你又要如何寫得清楚?
- 如果是個複雜的物件型別時,結構又該如何先確定好?
- 另外還有函式的返回型別又該如何來寫?
利用Flow型別的定義方式,來解決這個小案例的問題,可以改寫為像下面的程式碼:
// @flow
function foo(x: number): number {
return x + 10
}
foo(`hi`)
複製程式碼
當使用非數字型別的值作為傳入值時,就會出現由Flow工具發出的警告訊息,像下面這樣:
[flow] Cannot call
foo
with`hi`
bound tox
because string 1 is incompatible with number 2. (a.getting-start.js:6:5)
如果是要允許多種型別也是很容易可以加標記的,假使這個函式可以使用布林與數字型別,但返回可以是數字或字串,就像下面這樣修改過:
// @flow
function foo(x: number | boolean): number | string {
if (typeof x === `number`) {
return x + 10
}
return `x is boolean`
}
foo(1)
foo(true)
foo(null) // 這一行有型別錯誤訊息
複製程式碼
在多人協同開發某個有規模的JavaScript應用時,這種型別的輸出輸入問題就會很常遇見。如果利用Flow工具的檢查,可以避免掉許多不必要的型別問題。
真實案例
可能你會認為Flow工具只能運用在小型程式碼中,其實不然,Vue原始碼中大量使用flowjs中型別檢測:
Flow使用
- flow init
- // @flow 或 /* @flow */
- IDE外掛 或 flow check
在Visual Studio Code中因為它內建TypeScript與JavaScript的檢查功能,如果要使用Flow工具來作型別檢查,需要在使用者設定中,加上下面這行設定值以免衝突:
“javascript.validate.enable”: false
4 . babel外掛在編譯時就會一併轉換Flow標記
{
"plugins": [
"transform-flow-strip-types"
]
}
複製程式碼
Flow支援的資料型別
Flow支援原始資料型別,如下面的列表:
- boolean
- number
- string
- null
- void
型別別名&常見語法
// @flow
export type Test = {
titleOne?: string,
titleTwo: ?string
}
var a: Test = {titleOne:"3",titleTwo:4}
var b:string = ""
//any
export type NavigationGestureDirection = `horizontal` | `vertical`;
type T = Array<string>
var x: T = []
x["Hi"] = 2 //有Flow警告
type TT = Array<Test>
var xx:TT = []
xx = [{titleOne: `1`,
titleTwo: false}]
type MyObject = {
foo: number,
bar: boolean,
baz: string,
};
let val:MyObject = {foo:2,bar:false,baz:`444`};
var val1: MyObject = {foo:2,bar:false,baz:null};
var val2: MyObject = {foo:2,bar:false};
function method(val: MyObject):MyObject { return {foo:2,bar:false,baz:`2`}}
class Foo { constructor(val: MyObject) { /* ... */ } }
複製程式碼
React中的應用
如果你在React class裡面使用了React.PropTypes規範,你可以對JSX上的attributes做靜態型別檢查:
var Hello = React.createClass ({
propTypes: {
name: React.PropTypes.string.isRequired
}
...
});
//<Hello/> //Flow就會發現 缺少屬性的錯誤
//<Hello name={42}/>//屬性型別的錯誤
複製程式碼
import * as React from `react`;
type Props = {
foo: number,
bar?: string,
};
function MyComponent(props: Props) {
props.doesNotExist; // Error! You did not define a `doesNotExist` prop.
return <div>{props.bar}</div>;
}
<MyComponent foo={42} />
複製程式碼
更多關於支援React的細節 請移步 https://flow.org/en/docs/react/components/