为什么说 JavaScript 不擅长函数式编程

 我来答
阳光上的桥
2017-08-17 · 知道合伙人软件行家
阳光上的桥
知道合伙人软件行家
采纳数:21423 获赞数:65813
网盘是个好东东,可以对话和传文件

向TA提问 私信TA
展开全部
主要有以下原因:
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 的问题很深, 网上找解释吧, 我还没理解清楚, 只了解到满足了方便却造成了意料之外的复杂度.
A 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 写出来的代码几乎都是不符合函数式编程的限制得.
育知同创教育
2018-06-27 · 百度知道合伙人官方认证企业
育知同创教育
1【专注:Python+人工智能|Java大数据|HTML5培训】 2【免费提供名师直播课堂、公开课及视频教程】 3【地址:北京市昌平区三旗百汇物美大卖场2层,微信公众号:yuzhitc】
向TA提问
展开全部
JavaScript 很适合函数式编程, 比如自带的数组操作方法常常能串联出比较漂亮的写法, 而且 React 在社区就算不能通吃, 但是已经取得了如此广泛的影响, 让大量的开发者接受了 reducer 纯函数这样的观念, 并在组件抽象上用于很多函数式编程的手法, 逐渐构建了强大的技术栈. 最终, 通过这些来验证 JavaScript 在函数式编程使用上的成功, 某种程度上算是自圆其说了, 而且也做出了成绩.
但是这种理解从不同的角度观察, 还是存在问题的. 我从比较早就接触到了 CoffeeScript 以及深刻影响到它的语言: Haskell. 到现在, 我有三年多 CoffeeScript 开发的经验, 一年的 ClojureScript 小项目的经验, 以及勉强入门的 Haskell 学习经验. 站在 JavaScript 之外, 看到的情况跟在 JavaScript 社区内部看到的并不一样.
本回答被网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式