亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么說 JavaScript 不擅長函數式編程?

為什么說 JavaScript 不擅長函數式編程?

浮云間 2019-01-29 06:06:02
為什么說 JavaScript 不擅長函數式編程
查看完整描述

2 回答

?
蝴蝶刀刀

TA貢獻1801條經驗 獲得超8個贊

123456789101112131415161718主要有以下原因:Functions as first-class values, 函數一等公民, 可以把函數作為參數傳遞, 從而構造出高階函數各種用法. 這個用法各種語言都支持了: Lua 支持, Python 似乎也支持, Java 也開始支持了, 我會的語言少都舉不出來不支持傳函數的流行語言.Pure functions, 純函數. 可以寫, 但也有很大區別. JavaScript 沒限制, 從而不能預判函數純或者不純. Clojure 遵循 Lisp 風格的約定, 帶副作用的函數一般用 `f!` 這種嘆號結尾的寫法命名, 而編譯器沒有約束. Haskell 是嚴格約束的, 出了名的 IO Monad 就是因為遵循純函數導致副作用難以直接用數學函數表達出來, 最終精心設計出一個概念.Referential transparency, 引用透明, 所以表達式可以被其運算結果完全替換掉, 也就是要求控制甚至避免副作用. Controlled effects, 受控的副作用, 主要手段是隔離. JavaScript 需要人為地去隔離, 語言層面完全沒有限制. Clojure 也需要人為隔離, 就像前面說的 `f!` 那樣的約定, 同時規定了數據不可變, 再加上作者有意在語言中強調控制副作用, 實際上副作用少得多. Haskell 通過類型系統限定, 不隔離副作用無法通過編譯的.Everything is an expression, 一切皆是表達式. JavaScript 做不到, 導致設計 DSL 時候很頭疼, 倒是 CoffeeScript 做到了. Clojure 繼承了 Lisp, 很明顯一切皆是表達式. Haskell 代碼里都是函數, 除了類型聲明和語法糖部分, 也是一切皆是表達式.No loops, 換句話說, 不能用 for/while, 因為這兩個寫法當中的 `i++` 依賴可變數據. JavaScript 經常使用 for/while. Clojure 當中的循環基本上用尾遞歸實現, 同時也提供了 doseq 之類的 Macro 讓循環過程很好寫. Haskell 就是完全尾遞歸的寫法了.Immutable values. JavaScript 默認可變, 僅有的手段用 `Object.freeze` 可以強行鎖定對象或者 const 鎖定變量本身, 另外就是 immutable-js 那樣的共享結構的不可變數據作為類庫來實現. Clojure 是把不可變數據和結構共享作為語言的基礎, 專門設計了 Atom 類型用于模擬共享的可變狀態, 也不排除某些場景和宿主語言的互操作還是會有可變數據. Haskell 默認就是不可變數據, 也有 IORef 相關的代碼可以模擬可變狀態, 但在教程里幾乎看不到.Algebraic Datatypes, 代數類型系統. JavaScript 沒有靜態類型系統, TypeScript 有類型, 但和代數類型還不一樣. Clojure 沒有靜態類型系統, 就算有而只是很基礎的類型檢查, 或者用 Specs 做詳細運行時檢查. Haskell 有強大的代數類型系統, 即便是副作用也被涵蓋在類型系統當中.Product types. Haskell 通過代數類型系統支持.No Null. JavaScript 當中有 undefined 和 null. Clojure 當中只有 nil. Haskell 里沒有 null 也沒有 nil, 而是用了 Maybe Monad 這樣的概念, 通過類型系統進行了抽象和限制. null 的問題很深, 網上找解釋吧, 我還沒理解清楚, 只了解到滿足了方便卻造成了意料之外的復雜度.function always returns a value, 函數永遠都有返回值, 類似一切皆是表達式那個問題. 比如 Haskell 里會有的叫做 Unit 的 `()` 空的值. 這個有點費解...Currying, 柯理化. JavaScript 和 Clojure 也能模擬, 而在 Haskell 當中是默認行為.Lexical scoping, 詞法作用域. 三者都支持.Closures, 閉包, 都支持.Pattern matching, 模式匹配. 類似解構賦值之類的在 JavaScript 和 Clojure 當中通過語法糖也算有這個功能, 但是跟 Haskell 以及 Elixir 當中的用法對比起來差距很大. 比如說 Haskell 甚至能定義 `let 1 + 1 = 3` 來覆蓋 `+` 的行為, 雖然是奇葩的現象, 但這就是一個定義的 pattern, 在 JavaScript 和 Clojure 都沒有這種情況.Lazy evaluation, 惰性計算. JavaScript 是嚴格求值的, 不支持惰性計算. Clojure 支持 Lazy, 然而由于 Clojure 又允許了一些副作用, 實際上某些特殊場景會需要手動 force 代碼執行, 也就是說不完美. Haskell 采用惰性計算. 惰性計算就是說代碼里的表達式被真正使用來才會真正執行, 否則就像是個 thunk, 繼續以表達式存儲著. 我印象里 Elm 社區說過, 對于圖形界面來說 Lazy 反而是多余的.    大致做個總結, 就是 Haskell 當中的類型系統, 不可變數據, 控制副作用, 在 Clojure 當中只是做了不可變數據, 同時稍微控制了一下副作用, 而這些概念在 JavaScript 當中很少有支持. 這樣的結果, JavaScript 寫出來的代碼幾乎都是不符合函數式編程的限制得.



查看完整回答
反對 回復 2019-03-16
  • 2 回答
  • 0 關注
  • 674 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號