Cannot set calling scope vars from closures when localMode="modern"
Description
Environment
Activity
Adam Cameron 7 June 2021 at 17:10
Just in case this helps, here’s a slight variation that was biting me (from https://gist.github.com/adamcameron/6d845b09bbe1c2c7b9383a3f43d6dbe9 )
<cfscript>
function f() {
var one = "tahi"
var red = "whero"
setInVariablesScope({one=one, red=red})
}
function setInVariablesScope(locals) {
locals.each((key, value) => {
variables[key] = value
})
}
f()
writeOutput(variables.keyExists("one") ? variables.one : "one not set in variables")
writeOutput("<br>")
writeOutput(variables.keyExists("red") ? variables.red : "red not set in variables")
</cfscript>
Lucee:
one not set in variables
red not set in variables
ColdFusion 2021:
tahi
whero
Somewhat contradicting @Michael Offner 's earlier comment that “…the local scope is always prefered…”, the code above can be sorted out by using an intermediary variable:
function setInVariablesScope(locals) {
vars = variables
locals.each((key, value) => {
vars[key] = value
})
}
So Lucee’s closing over vars
just fine. Just not variables
.
Zac Spitzer 7 June 2021 at 15:21(edited)
<cfscript>
function outer() {
local.a=1;
var zzz = function () {
variables.a=2;
variables.b=2;
};
zzz();
writedump(local);
writedump(variables);
}
outer();
</cfscript>
In Lucee the variable scope prefix on lines 5 & 6 has no effect
JP 21 May 2021 at 22:04
@Matthew Clemente what I don’t like about the workaround is the additional programming bits for something that should just work, no matter what mode you’re in. To me those kinds of directives seem problematic from a code maintenance standpoint. Anyone who looks at that code 5 years from now will be scratching their heads.
Matthew Clemente 21 May 2021 at 21:42
Thanks @JP
I do see that override working on my example function - here’s the syntax: function example() localmode = "classic" {
Interestingly, it doesn’t work if I put the override on the anonymous function within the each()
, I guess because the parent function there is still operating with localMode="modern"
.
Agreed that it’s a less than ideal workaround. Ideally, I think, the local scope would be preferred, but I there would be a syntax to explicitly modify parent scope variables, such as explicitly scoping to variables
.
JP 21 May 2021 at 21:26
@Matthew Clemente afaik you have to explicitly override the “modern” setting in your closure function definition by adding localmode='classic'
to the definition. It’s a terrible workaround but I believe it works. I have not tried it and I’m not entirely sure what the syntax is, but from what I’ve been told it works. I just turned off modern localmode instead of dealing with it. I only was using modern localmode as a fallback to code mistakes when a variable was inintentionally not declared.
Details
Details
Assignee
Reporter
Priority
Labels
New Issue warning screen
Before you create a new Issue, please post to the mailing list first https://dev.lucee.org
Once the issue has been verified, one of the Lucee team will ask you to file an issue
Consider this code:
<cfscript> function doSomething ( any onComplete) { writeOutput("<p>I just did something!</p>"); onComplete(); } function test () { var myVar = ""; writeOutput("<p>Before closure: [#myVar#]</p>"); doSomething(function () { myVar = "test"; }); writeOutput("<p>After closure: [#myVar#]</p>"); } test(); </cfscript>
In application.cfc, when this.localMode="modern", the test fails to persist the change made to myVar. When turned off, it works as expected.