我想编写一个程序,为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
)
但这不起作用,因为def
和defn
在模块的范围内定义变量,而不是定义use-div-for-a-cool-program
的本地变量。如果我使用不同的div多次拨打use-div-for-a-cool-program
,则所有新的def
和defn
都会覆盖旧的每次。
一种解决方案是使用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)))
有更好的解决方案吗?
答案 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)))
简而言之,我会避免隐式处理任何状态或可变值(counter
或div
)。如果你必须渲染一个取决于某些变化状态的视图,我会推荐任何Clojurescript React包装器,如Om或Reagent。