Reason是什么
Reason是facebook团队开发的OCaml的一种新语法,其目的是给OCaml类似JavaScript的语法以便JavaScript程序员使用,并通过BuckleScript编译成JavaScript。
同时ES新特性的很多草案是从OCaml中引入的,比如管道操作符 |>
,直接使用Reason能提前爽到这些东西(官方称这些特性在ECMAScript中甚至排到了ES2030),并且Reason也能享受到Javascript和OCaml的生态,例如你依然能够使用npm/yarn,或者opam。在构建应用方面,也有ReasonReact等库。
特性
- 类型有着100%的覆盖率,能自动推断类型,一旦编译,类型会保证是明确的
- 在大多数是纯函数(pure)、不可变(immtuable)、函数式(functional)的同时,有可选的副作用(side-effect)和数据突变(mutation)
- 编译快速且产生的体积小
- 能转换使用js的代码,同时依然能用js的生态系统
与JavaScript语法的联系
虽然Reason很大程度上是提供给JavaScript开发人员使用的,但实际上其很多语法设计是基于OCaml,在实际使用中有不少地方值得注意。
句末的分号
JavaScript中句末分号是可选的,Reason中分号有自己的规定,有些地方必须有分号,有些地方不可以用分号,并且是有着意义的。
类型
JavaScript是典型的弱类型语言,也出现过TypeScript或者Flow等语言或工具来增强JavaScript的类型。在这一点上,Reason本身是能自动推断类型的,同时也可以使用:
去标注类型,例如let foo: int = 10;
。这种形式和TS、flow非常相似。
不过Reason没有泛型的概念, 要想声明类似一个又有int又有float的tuple需要这样
Reason的list的元素必须为同一类型,所以不需要List<String>
这种操作了。
let关键字
JavaScript在ES6中引入的let关键字用于声明块级作用域的变量,相同的是Reason中也有块级作用域这一概念,但是在Reason中let强调的是绑定(binding)这一概念,let绑定的变量是immtuable的.这意味这声明变量以后无法简单的直接修改值(类似es6的const关键字)。要想类似es6的let关键字声明可变变量的话,可以使用ref关键字,
注释
Reason中不能使用//
单行注释,请使用/* */
。
单引号和双引号
在JavaScript中没有字符和字符串的区别,单双引号区别也基本只是在转义上。
Reason中有字符串(String)和字符(Characters)的区别,字符串使用双引号,字符使用单引号。
+操作符的使用
在JavaScript中+
不仅是加法运算,同时也可以是链接字符串的操作符,而且有着比如”1”+2 = “12”这种很神奇的类型转换的问题。
而在Reason中,单独的+
用于整数加法运算,浮点数的加法运算会使用+.
,链接字符串使用++
。
Reason中其他运算符,整数型和浮点型也是不同的操作符,浮点型的操作符会多一个.
。
===、==和布尔值
JavaScript中两种等的区别为是否严格相等,===
除了会去比较值以外还会去比较类型和引用。
Reason中的==
叫做physical equal,===
叫做 referential equal,==
会去深检查数据结构,===
只会检查引用(浅检查, 和js的===是类似的)。
实际上在Reason通过bucklescript编译成JavaScript的后的代码里,==
会被编译成Caml_obj.caml_equal()
,===
依然是===
。
Reason的布尔值true,false表面上和JavaScript的布尔值是一回事,但在编译中会被编译为Js.true_
和Js.false_
。
null undefined
JavaScript中很有名的null和undefined,null表示不存在,undefined表示未定义,在Reason中统统用None代替了。
数据结构类型
JavaScript是基于原型的语言,所有类型都能追溯到Object.prototype,这导致无论是Array还是Object都在一定程度上有着相似性,比如Array和Object都是mutable的,这也是我们需要immtuable.js的原因之一。同时ES6中引入了迭代器(iterator)的概念,Array,Map,Set等都是基于迭代器实现的。
而Reason中的数据结构多种多样,其中最类似JavaScript里Object的Record则是默认immutable的。用[]
创建的数据结构在Reason中是List,是immtuable的,创建Array则需要使用[||]
。
需要注意的是,Reason的list里的元素必须为同一类型,Array为定长,这点与JavaScript有很大区别。
函数
JavaScript中使用function关键字声明函数,在ES6中引入了箭头函数(Arrow Function)。Reason中没有function关键字,使用=>
来声明函数,装参数的括号不可以省略,参数可以使用~
来标注到对应的参数名(像是React中给某个组件传递props然后调用该组件的this.props.名字去获取props的值)。
由于Reason是函数式的,没有return
关键字,函数都可以部分调用,很方便科里化(currying),例如
因此要注意的是,调用函数时传递的参数少于函数参数的个数时并不是不传递参数而是部分调用该函数,要想实现类似JavaScript中可选参数的话需要使用?
来标注可选参数。
循环
在没有Array.map等函数或者迭代器的年代要用原生js遍历数组得老老实实写for循环,不过在Reason中for循环非常简单
while循环则是大致相同
解构(Destructuring)
ES6的解构为书写代码带来了很大的便利,同时如果用的非常熟练,能写出很多简介明了的代码,Reason中同样支持了解构,并且大致与ES6的解构相似。非要说不一样的话那就是多了元组(tuple)的解构
switch
二者的switch都是拿来替代大量的if/else结构,不过Reason的switch功能结合Variant和Pattern Matching使用会变得非常强大,能极大程度的让代码变得简洁优雅,不过这里只是比较与JavaScript的异同,就不展开讲了。
异常
JavaScript能使用throw抛出异常或者try catch finally结构来处理异常,Reason中使用raise来抛出异常,使用try foo { |Err => something}来处理异常,不过要注意没有finally。
JSX
JSX能够使用类似html标签的语法来使用组件,Reason依然继承了这个特点,但在某些语法上有略微不同。
JSX中props如果只写props名字,则会被当作true,比如<myInput checked />
, 而在Reason中这会被解释为<myInput checked={checked} />
。这一点被称为Argument punning。另外,如果是使用jsx来开发react应用的话,已经有ReasonReact了。
转化为js
Reason可以使用[%%bs.raw {||}]
或[%bs.raw {||}]
来将执行js的代码(这里的一个%和两个%区别很大,暂不展开讲)。
总结
Reason有着大量OCaml优雅语法的优点,同时对JavaScript开发者非常友好,相应的生态也有非常好的支持,对于爱折腾的个人开发者来说不失为一个有趣好玩的新鲜事物。