D isn't the only language with janky closure semantics
H. S. Teoh
hsteoh at qfbox.info
Fri Aug 29 22:28:17 UTC 2025
I happen to be working on some JS today (gasp!), and ran into this odd
behaviour. Code snippet:
```js
function buildDialog(label, buttons) {
const dlg = document.createElementNS(...);
...
for (button of buttons) {
const btnElem = document.createElementNS(...);
...
btnElem.addEventListener(click, (ev) => {
button.action(ev); // <--- this was acting up
});
}
}
buildDialog("My dialog", [
{
label: "Yes",
action: (ev) => {
alert("Yes");
}
},
{
label: "No",
action: (ev) => {
alert("No");
}
}
]);
```
Running the above dialog, it seems that clicking on either button calls
the "No" callback. For some reason, the "Yes" callback never gets
called. This left be scratching my head for a while, until I thought of
the similar situation in D:
```d
struct Button {
...
void delegate(Event) action;
}
void delegate(Event)[] actions;
foreach (button; buttons) {
actions ~= (event) => button.action(event);
}
```
As anyone who's run into a similar issue would immediately notice, the
delegate here closes on the loop variable `button`, which unfortunately
shares the same stack location across loop iterations, so that the call
to `button.action` doesn't call the action of the *current* instance of
Button, but the one that gets assigned to the last loop iteration.
Suspecting a similar issue in the JS code, I modified the
addEventListener call like this:
```js
const action = button.action;
btnElem.addEventListener(click, (ev) => {
action(ev);
});
```
and voila! Now the code works! So apparently, copying button.action
into a local variable makes the lambda close properly over an isolated
instance of button.action rather than closing over the shared loop
variable `button`, which gets overwritten by each subsequent loop
iteration.
//
Now, JS is certainly not a model language to follow, but it's
interesting how it shares with D the same issue over closures involving
loop variables.
I guess that means we're not alone. :-P Even though this situation is
definitely not ideal.
T
--
Don't throw out the baby with the bathwater. Use your hands...
More information about the Digitalmars-d
mailing list