# jsbind **Repository Path**: mirrors/jsbind ## Basic Information - **Project Name**: jsbind - **Description**: jsBind 是一个用来帮助开发者构建 MVVM Web 网站和应用,使用 JavaScript 和 TypeScript - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: https://www.oschina.net/p/jsbind - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-18 - **Last Updated**: 2023-09-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # jsbind Create bindings to JavaScript that work when Nim is compiled to JavaScript as well as when compiled to Asm.js (through [Emscripten](http://emscripten.org)). Here's an example of how `XMLHttpRequest` can be defined: ```nim import jsbind type XMLHTTPRequest* = ref object of JSObj # Define the type. JSObj should be the root class for such types. proc newXMLHTTPRequest*(): XMLHTTPRequest {.jsimportgWithName: "function(){return (window.XMLHttpRequest)?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP')}".} proc open*(r: XMLHTTPRequest, httpMethod, url: cstring) {.jsimport.} proc send*(r: XMLHTTPRequest) {.jsimport.} proc send*(r: XMLHTTPRequest, body: cstring) {.jsimport.} proc addEventListener*(r: XMLHTTPRequest, event: cstring, listener: proc()) {.jsimport.} proc setRequestHeader*(r: XMLHTTPRequest, header, value: cstring) {.jsimport.} proc responseText*(r: XMLHTTPRequest): jsstring {.jsimportProp.} proc statusText*(r: XMLHTTPRequest): jsstring {.jsimportProp.} proc `responseType=`*(r: XMLHTTPRequest, t: cstring) {.jsimportProp.} proc response*(r: XMLHTTPRequest): JSObj {.jsimportProp.} ``` Use the bindings as normal Nim functions. They will work in both JS and Asm.js targets. ```nim proc sendRequest*(meth, url, body: string, headers: openarray[(string, string)], handler: Handler) = let oReq = newXMLHTTPRequest() var reqListener: proc() reqListener = proc () = jsUnref(reqListener) handler(($oReq.statusText, $oReq.responseText)) jsRef(reqListener) oReq.responseType = "text" oReq.addEventListener("load", reqListener) oReq.open(meth, url) for h in headers: oReq.setRequestHeader(h[0], h[1]) if body.isNil: oReq.send() else: oReq.send(body) ``` # Low-level Emscripten bindings `jsbind.emscripten` module defines the types and functions that emscripten defines along with some useful macros and pragmas such as `EM_ASM_INT`, `EM_ASM_FLOAT`, `EMSCRIPTEN_KEEPALIVE`, etc. ```nim import jsbind.emscripten proc foo() {.EMSCRIPTEN_KEEPALIVE.} = # now it's possible to call this function from JS discard EM_ASM_INT(""" alert("hello, world!"); """) # Use EM_ASM_* like you would do it in C ``` # How jsbind works When compiling to JavaScript, `jsbind` does almost nothing, translating its pragmas to corresponding `importc`, `importcpp`, etc. Basically there is no runtime cost for such bindings. The real magic happens when compiling to Emscripten. The imported functions are wrapped to `EM_ASM_*` calls, inside which the arguments are unpacked to JavaScript types as needed, and their return values are packed back to Asm.js. # Passing closures to imported functions Closures are special because they have environment that can be garbage collected when no references to the closure left. Consider the example above with `sendRequest`. The `reqListener` is passed to Emscripten/JS function and no references are left after `sendRequest` returns. Here we need to explicitly protect it from collection with `jsRef`, and not forget to unprotect it when it is no longer needed with `jsUnref`, otherwise it will leak. `jsRef` and `jsUnref` do nothing when compiled to JavaScript, still its a good practice to place them where appropriate to make your bindings compatible with Asm.js.