什么是ClojureScript相当于JavaScript范围的参数

时间:2015-01-21 08:23:03

标签: closures clojurescript

我想编写一个程序,为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)))

有更好的解决方案吗?

1 个答案:

答案 0 :(得分:1)

我的解决方案不是您想听到的:)您将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),它也会让你的意图变得清晰。当我想要返回函数并稍后调用它时,我使用了一个闭包,记住它的定义范围。我没有看到如何将这些函数定义为闭包并在之后精确调用它们。

如果你想保持一些状态与你的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包装器,如OmReagent