The CLJS compiler will generate a lot of unnecessary function wrappers for functions created inside a {{let}} (and in certain cases outside). These function wrappers are only necessary when inside a {{loop}} or when the function uses {{recur}} to work arround quirks in JS {{var}}. As far as I can tell this commit [1] changed the behavior to instead always do it.
This can generate quite a lot of "extra" code that the Closure Compiler will not eliminate.
A dummy example shows that these wrapper fns can get quite excessive argument lists even if they don't use any of the locals that function wrapper is meant to preserve.
(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))
))
The generated code
// Compiled by 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';
// this could just be 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
I didn't do any benchmarks yet but I'm pretty sure this has a certain runtime overhead as well.
The code that causes this should be optimized to only emit those wrappers when actually needed and also only for the locals that were actually used.
Alternatively we could use JS {{const}} (or {{let}}) since nowadays >98% of Browsers in use support this [2] and the Closure Compiler will generated those function wrappers for us with certain {{:language-out}} settings (ie. the current default would).
[1]
https://github.com/clojure/clojurescript/commit/78d20eebbbad17d476fdce04f2afd7489a507df7
[2]
https://caniuse.com/#feat=const