How to concisely assign and immediately invoke a function variable?
Asked Answered
T

5

5

The following is a method for defining an anonymous function within a closure, invoke the function, and forget it:

(function () { "do stuff"; })();

This is used to maintain a limited scope without adding bulk to the script (IIFE: Immediately-Invoked Function Expression).

What if you're hoping to immediately execute a function, while still retaining the function for future use, like the following:

var doThing;
(doThing = function () { "do stuff"; })();

This works in the browsers I've tested (Chrome, FF, IE8, IE10), but this does not pass JSLint (Bad Invocation). Are there any compatibility issues with doing it this way?

Is there a way of accomplishing this that is looked kindly upon by JSLint?

Tolmann answered 10/9, 2012 at 20:27 Comment(2)
If your function body was return 10;, wouldn't doThing be defined as 10 instead of the function itself?Wystand
@Cory No, the parens are around the assignment. Easy enough to test, right?California
L
7

If passing jslint is absolutely necessary then:

var doThing;
(doThing = function () { "do stuff"; }).call();

should do the job.

Edit

For passing params during .call

var doThing;
(doThing = function (param1, param2) { 'do stuff'; }).call(this, arg1, arg2);
Lettie answered 10/9, 2012 at 20:43 Comment(3)
Awesome! The whether this is actually useful is still up in the air, but this definitely answers the question. Thanks!Tolmann
Same length as: var doThing = function() { "do stuff"; }; doThing(); with same end result but far less clarity (but yes, answers the question)Stonework
You're right; it's not practical. Better to try to find a new method of solving old problems, and fail, than to resolve to always use the same method you've always used. Failure promotes progress.Tolmann
S
3

You should not assign a function reference and invoke the function at the same time. This kind of thing would easily cause heads to explode, as it would add to the already existing possibilities of assigning a variable to a reference, assigning a variable to a return, or assigning a variable to the result of an assignment. The way to do so anyone else would be able to read your code would be to do it in 2 lines. If you want to wrap that in a function it would be something like:

var doThing = (function() {
  var inner = function() {
     //doThing body
  };
  inner();
  return inner;
})();

That by itself buys you nothing but obfuscation over doing it the simple way since the invocation wouldn't stick around regardless. You could however, if you really wanted create a function that does this:

var callAndRef = function(funk) {
  funk();
  return funk;
};

and then

var doThing = callAndRef(function() {
//doThing body
});

or if you were feeling even fancier and have JS gurus working with you, you could throw it on to the Function.prototype and chain it off the declaration. You can also pepper in call/apply to maintain the this reference and arguments (but it seems like that would just be noise for your present question).

All of this should be done with extreme prudence it's certainly not worth going this route to save yourself having to type a couple extra lines of one command each.

Stonework answered 10/9, 2012 at 20:32 Comment(3)
This question is more on the side of curiosity than practicality :) . I like this response, but I think CD Sanchez's code more directly answers the question. I'm curious as to how one performs over the other, though.Tolmann
His would most likely perform better and is more concise. The disadvantage is that it takes advantage of using the result of an assignment which generally seems to be discouraged in the languages that support it as it greatly confuses novices and can still trip up those with experience.Stonework
+1, jslint complains about the line of code because it's purposefully confusing. js is devious enough without developers actively working to obfuscate it.Suggest
D
3

A slight reordering of your code will help.

var doThing = function () { "do stuff"; };
doThing();

or

var doThing = function () { "do stuff"; };
(doThing)();
Declassify answered 10/9, 2012 at 20:34 Comment(8)
That is actually slightly different ... var doThing = function () { "do stuff"; }; doThing = doThing(); would be the equiv of what the OP is looking for. That said, it is an awkward and non-standard way to write it.Antlia
@JeremyJStarcher He wants to immediately invoke a function, and save it for future use. That is exactly what the code I wrote does...Declassify
@dqhendricks: But he also wants to "maintain a limited scope". You're executing doThing() in the global scope.Wystand
This is the same functionality I'm hoping to achieve, I was just curious as to whether the actual variable declaration and invocation could be accomplished in one statement.Tolmann
@Tolmann You could return arguments.callee from within your function, which would basically return the function itself in order to do what you are hoping to do, but it is being debated whether arguments.callee will be depreciated or not in future ECMA script versions.Declassify
@Tolmann basically var somefunction = ( function() { // do something; return arguments.callee; } )();Declassify
The immediate disadvantage of that method is that if you were to perform var result = somefunction(); result would hold the function itself, not the result of "do something".Tolmann
@Tolmann Yep. So if you need an alternate return value, there is no practical way to do this in one line, and the above answer would probably be best.Declassify
P
0

If you only want to fix the Bad Invocation problem you can do this:

var doThing;
doThing = function () { "do stuff"; };
doThing();

By splitting the declaration and assignment you make your code easier to understand and to maintain. Another option is to declare a function with a name:

function doThing() { "do stuff"; }
doThing();
Plesiosaur answered 10/9, 2012 at 21:15 Comment(0)
A
-1

JSLink is Crawford's personal opinion on how Javascript should be written. Most of it is excellent advice, but sometimes his opinion seems to get into the way.

This is the more "standard" way to write that line:

var doThing = (function () { "do stuff"; }());

Note the whole (function ...) is inside the parens.

Antlia answered 10/9, 2012 at 20:34 Comment(3)
True Crockford suggests that, but it does not answer the question. How to invoke AND call it later.Tan
@Tan -- Then I must be misunderstanding the question.Antlia
'doThing' doesn't bind to the function with this code. However, I would tend to agree that Crawford's opinion shouldn't be treated too religiously. The OP is looking to save the closure from the function in doThing, and invoke it (at least that's how I interpreted it).Perpetual

© 2022 - 2024 — McMap. All rights reserved.