I feel like this may be a silly question, but I st...
# cfml-general
s
I feel like this may be a silly question, but I struggle to use dynamic values in closure functions. It seems like such an obvious use-case, but every bit of documentation or example I can find uses static values in the inner function. I know that I can let the inner function just search through the outer scopes until it finds a variable, but that just doesn't feel right to me. I feel like I should be able to explicitly provide a value to the function, like an argument default. Anyone know if this, or something similar, is possible? e.g. I feel like this should work, but doesn't...
Copy code
<cfscript>
    myArray = ['foo','foo','foo','bar'];
    myCriteria = 'bar';
    filteredArray = myArray.filter(function(myArrayVal, criteria = myCriteria){
    	return Arguments.myArrayVal == Arguments.criteria;
    });
    writeDump(filteredArray);
</cfscript>
m
What about it doesn't work? I believe the second argument in
array.filter()
is its index.
and the third is the array itself. What happens if you try
function(myArrayVal, i,a,criteria = myCriteria)
?
s
Ahh, yes... I see that now, I hadn't noticed that the second argument is the index... should have read the docs more closely. I guess I'm also mixing named and ordered arguments. Your suggestion doesn't work either, but I suppose I shouldn't expect it to... But, what's the alternative? If I'm using this in a function, as I almost always do, then I usually want to filter based on an argument that's passed to the outer function... but I can't access the outer function's arguments scope. So, I have to put it into the outer function's local scope and let the inner function search for it. Just feels inelegant.
m
I'm not sure what the best answer is. I've ended up doing the "resave argument as local variable" method too.
w
fwiw, in the closure you're doing a comparison with = and it should be ==.
s
Well, glad it's not just me then!! Just doesn't feel right having to do that when the variable is already there. I wonder if there's a reference to the outer variables scopre in there somewhere.
Hah! Bloody hell @websolete, you're quite right... I just hacked that together for an example. And now that's fixed, @Myka Forrest's first suggestion does indeed work!! Now if only I didn't have to put two innocuous arguments in there before the dynamic one...
m
BUT, according to MDN, that's exactly what closures are for.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
s
Sure, but is there a specific 'outer' scope that we could use? So, if I'm looking for the
arguments
scope of the outer function, I could use something like
outer.arguments.myvar
? That'd be super helpful.
m
Not that I know of.
s
Shame... that'd be killer.
Well, thanks guys... at least I know that A) I'm not doing it wrong, and B) There is a way to do what I want, just one that makes the function hella ugly!
m
CFDocs' closure page doesn't scope its variables, which is a little disappointing to me. https://cfdocs.org/closures
s
Yeah, and a little confusing when you're coming to closures for the first time... Took me a while to get my head around the concept. Also, I read somewhere that actually scoping the inner function can really increase performance on a large dataset.
a
s
Correct. But not really what I wanted to achieve...
This, as suggested by @Myka Forrest, is more like what I was after: https://trycf.com/gist/f5bf5b4fd675ce43b6e1165fcab085a2/lucee5?theme=monokai
The point being that I didn't want to have to specify the
myCriteria
value elsewhere, because in most cases it already exists my outer function's arguments scope. So I'd want to use
myCriteria = Arguments.criteria
.
...and then I get to scope the value in the inner function too, which feels nicer.
a
That is an unsupported usage. ArrayFilter supports the following:
function(item [,index, array]){}
Where this stuff is powerful is that you can compose filters and pass them in as functions. Something like this: https://trycf.com/gist/a0410bec470b54d87d52eeee0599aad7/lucee5?theme=monokai
So the criteria is being passed around with the function - it is enclosed in the closure.
s
Your second link is the same as the first, have you saved changes?
a
hmm - maybe didn't copy correctly - in the meantime here is the Javascript version for comparison https://jsbin.com/xiqidekaqo/edit?js,console
s
Wow... ok, that's interesting!
It does seem like extra work, but it's very elegant...
a
It's not really more work, but it is a shift in thinking and very powerful.
1
s
Well I mean, calling the
createFilter
function each time is similar to just pushing the criteria into the local scope, which I was trying to avoid... but I really like the code-zen of it, and I'd imagine it has better performance than searching the outer scope.
I appreciate that anyway... I've seen similar examples but I've not really been able to grasp it, passing the function to the function. Now I have that in the context of what I was trying to do, it makes more sense.
a
I find it quite fun doing things like that. There are others on here that are way better at it than I am.
z
array filter in Lucee passes in (item, idx, srcArray )to the closure, I need to update the docs
👍 1
a
sorry - got caught up in work. If you think it's too much typing you can write it using arrow functions - I used the longer syntax in my previous example as it's easier to grok if you're not familiar with arrow functions. This is the same with arrow functions: https://trycf.com/gist/df87e20498aa6b02d7986dd7c8c7a729/lucee5?theme=monokai