绑定到全局 JS 值
首先,确保你要绑定的值不在我们提供的 API 中。
一些 JS 值,例如 setTimeout
,存在于全局作用域内。你可以像这样绑定到它们:
(我们已经在 Js.Global 模块中提供了 setTimeout
、clearTimeout
等绑定。)
上面的代码会绑定到 JavaScript 的 setTimeout
方法,和与之对应的 clearTimeout
方法。external
的类型标注指定 setTimeout
需要满足:
接受一个参数为
unit
且返回unit
的函数(它在 JS 端变成一个不接受任何内容且不返回任何内容的函数(指返回undefined
))。和一个调用回调函数的延时值。
返回一个数字,即 timeout 的 ID。这个数字可能会很大,所以我们将其建模为浮点数,而不是 32 位整数。
技巧和诀窍
以上情况并不理想。想想怎么让 setTimeout
返回的 float
来作为 clearTimeout
的输入。你没办法保证将 setTimeout
的返回值传递给 clearTimeout
!据我们所知,有人可能把 Math.random()
传递给后者。
我们正在使用具有出色类型系统的语言!让我们利用一个流行的特性来解决这个问题:抽象类型。
显然,timerId
是一个只能通过 setTimeout
创建的类型!现在我们可以保证 clearTimeout
将 收到一个有效的 ID。timerId
是否是数字只是一个实现细节。
由于 external
是内联的,我们最终得到的 JS 输出与手写 JS 一样可读。
全局模块
如果你想要绑定一个全局模块内的值,例如 Math.random
,可以将 scope
附加到被 val
标注的 external
:
你可以通过传递一个元组给 scope
来绑定一个任意深度的对象:
这将绑定到 window.location.ancestorOrigins.length
。
特殊全局值
像 __filename
和 __DEV__
这样的全局值可能会不存在;你甚至不能将它们建模为 option
,因为仅仅是在 ReScript 中引用它们(然后编译为 JS)就会触发常见的 Uncaught ReferenceError: __filename is not defined
错误,例如在浏览器环境中就会这样。
对于这些麻烦的全局值,ReScript 提供了一种特殊的方法:%external(a_single_identifier)
。
第一行的 typeof
检查不会触发 JS ReferenceError。
另一个例子: