CLJS 编译器将为 {{let}} 内部创建的函数(以及在某些情况下外部创建的函数)生成大量的不必要的函数包装器。只有在 {{loop}} 内部,或者当函数使用 {{recur}} 来处理 JS {{var}} 的怪癖时,这些函数包装器才是必需的。据我所知,此提交 [1] 已经更改了行为,使其总是这样做。
这可能会生成大量的“额外”代码,Closure Compiler 将无法去掉。
一个示例演示了即使这些包装函数没有使用到函数包装器应该保留的任何本地变量,它们也可以获得相当长的参数列表。
(ns test.app)
(defn other [x])
(defn dummy [{:strs [foo bar coll] :as p}])
(let [x "test"
y "test"
z "test"
a (fn [i] i)
b (fn [i] i)
c (fn [i] i)
(fn return []
(array a b c))
))
生成的代码
// 使用 ClojureScript 1.10.520 编译 {}
goog.provide('test.app');
goog.require('cljs.core');
test.app.other = function test$app$other(x) {
return null;
};
test.app.dummy = function test$app$dummy(p__526) {
var map__527 = p__526;
var map__527__$1 = (!(map__527 == null)
? map__527.cljs$lang$protocol_mask$partition0$ & 64 ||
cljs.core.PROTOCOL_SENTINEL === map__527.cljs$core$ISeq$
? true
: false)
: false)
? cljs.core.apply.call(null, cljs.core.hash_map, map__527)
: map__527;
var p = map__527__$1;
var foo = cljs.core.get.call(null, map__527__$1, 'foo');
var bar = cljs.core.get.call(null, map__527__$1, 'bar');
var coll = cljs.core.get.call(null, map__527__$1, 'coll');
var x = 'test';
var y = 'test';
var z = 'test';
// 这可以仅仅是 var a = function(i) { return i };
var a = (function(x, y, z, map__527, map__527__$1, p, foo, bar, coll) {
return function(i) {
return i;
};
})(x, y, z, map__527, map__527__$1, p, foo, bar, coll);
var b = (function(x, y, z, a, map__527, map__527__$1, p, foo, bar, coll) {
return function(i) {
return i;
};
})(x, y, z, a, map__527, map__527__$1, p, foo, bar, coll);
var c = (function(x, y, z, a, b, map__527, map__527__$1, p, foo, bar, coll) {
return function(i) {
return i;
};
})(x, y, z, a, b, map__527, map__527__$1, p, foo, bar, coll);
return (function(x, y, z, a, b, c, map__527, map__527__$1, p, foo, bar, coll) {
return function test$app$dummy_$_return() {
return [a, b, c];
};
})(x, y, z, a, b, c, map__527, map__527__$1, p, foo, bar, coll);
};
//# sourceMappingURL=app.js.map
我还没有进行任何基准测试,但我很确定这也存在一定的运行时开销。
导致此问题的代码应该被优化,只在实际需要时发出这些包装器,并且仅对实际使用的本地变量进行。
或者,我们可以使用 JavaScript 的 const(或 let)关键字,因为如今 >98% 的浏览器都支持这一点 [2],Closure 编译器会根据特定的 {{:language-out}} 设置(即当前默认设置)为我们生成这些函数包装器。
[1]
https://github.com/clojure/clojurescript/commit/78d20eebbbad17d476fdce04f2afd7489a507df7[2]
https://caniuse.cn/#feat=const