接受参数的 JavaScript 范围的 ClojureScript 等价物是什么

What is the ClojureScript equivalent of a JavaScript scope taking an argument

我想编写一个程序,向 div 添加复杂的行为(例如,考虑某种交互式图形)。我希望能够将 div 传递给库中的函数,并让库向 div.

添加行为

如果我在 JavaScript 中这样做,我会写以下内容。

my_page.html中:

<div id="program-container"></div>

<script src="my_library.js" type="text/javascript"></script>
<script type="text/javascript">
    useDivForACoolProgram(document.getElementById("program-container"));
</script>

my_library.js中:

function useDivForACoolProgram(div) {
    var x = 2;
    var y = 3;

    function setup() {
        div.innerHTML = "Getting ready to run...";
        doMainSetup();
    }

    function doMainSetup() {
        ...

    // Lots more functions, many of which refer to div
}

请注意,该库公开了一个接受 div 的函数。当我们向它传递 div 时,该库将其所有状态保存在与传递的 div 关联的闭包中,这可能允许我将此行为添加到许多 div 上如果我愿意的话,请翻页。

我想在 ClojureScript 中做同样的事情。我的第一次尝试如下:

(defn use-div-for-a-cool-program [div]
    (def x 2)
    (def y 3)

    (defn setup []
        (set! (.innerHTML div) "Getting ready to run...")
        (do-main-setup))

    (defn do-main-setup []
        ...

    ;; Lots more functions, many of which refer to div
)

但这行不通,因为 defdefn 在模块范围内定义变量,而不是在 use-div-for-a-cool-program 本地定义变量。如果我用不同的 div 多次调用 use-div-for-a-cool-program,所有新的 defdefn 每次都会覆盖旧的。

一个解决方案是使用 let 代替,但这有点不令人满意,因为它迫使我们在函数被引用之前给出函数的实现,而且也很难阅读,例如

(defn use-div-for-a-cool-program [div]
    (let [x 2
          y 3
          do-main-setup (fn []
                            ...)
          setup (fn []
                    (set! (.innerHTML div) "Getting ready to run...")
                    (do-main-setup))
          ;; Lots more functions, many of which refer to div
         ]
      (setup)))

有没有更好的解决方案?

我的解决方案不是您想听到的:) 您将 div 作为参数传递给每个函数。您不是隐式地将函数耦合到 div,而是明确指定需要 div:

(defn do-main-setup [div]
  ...)

(defn setup [div]
  (set! (.innerHTML div) "Getting ready to run...")

(defn use-div-for-a-cool-program [div]
    (let [x 2
          y 3]
      (do-main-setup div))
      ;; Lots more functions, many of which refer to div explicitly
      (setup div)))

即使这有点冗长(因为您每次都通过 div),但它使您的意图更加明确。当我想 return 函数并稍后调用它时,我使用闭包,记住它被定义的范围。我看不出将这些函数定义为闭包并在之后精确调用它们有何帮助。

如果您想保持与您的 div 相关联的某些状态,我也会明确地这样做。例如,如果您想统计 div 收到的点击次数,我会这样做:

(defn add-state-handler [div state]
  (set! (.onclick div) #(swap! state inc)))

(defn use-div-for-a-cool-program [div]
    (let [x 2
          y 3
          counter (atom 0)]
      (do-main-setup div))
      (add-state-handler div counter)
      ;; Other functions that reference div and counter
      (setup div)))

简而言之,我会避免隐式处理任何状态或可变值(counterdiv)。如果你必须渲染一个依赖于一些变化状态的视图,我会推荐任何 Clojurescript React 包装器,比如 Om or Reagent.