0

I just accidentally discovered this weird thing, and I am wondering if it's a bug, or an expected behavior.

let myfunc = function(a) {
     return function localScope(b) {
         console.log(a + b + " this")
     }
 }

(function () {
     console.log('test')
  })() 

The first function returns a function. I never invoke either of them.

Then below, I create a self-invoking function, and what happens is, the self-invoking function is not invoked, instead, it's passed as the a argument to the above myFunc parent function, and then somehow the myFunc is invoked, and for some reason, the returned localScope function is also invoked, and it consoles out the body of the self-invoking function.

However, this does not happen if I declare the self-invoking function differently by enclosing it with brackets, like this:

 (function () {
     log('test')
  }()) 

Is this like a bug, or is this an expected behavior? Why does declaring the self-invoking function differently makes a difference?

I am guessing this has to do with the scope object, but I have no idea what's happening here. Why is the self-invoking function passed as an argument, and why and how is the local function inside the parent invoked.

happy_story
  • 1
  • 1
  • 7
  • 17
  • Because you're missing the semicolon to separate the two statements. – Bergi Jul 26 '21 at 21:32
  • "*this does not happen if I declare the self-invoking function differently by enclosing it with brackets*" - it still does, it's just passing a different value. – Bergi Jul 26 '21 at 21:33
  • Related: [TypeError: console.log(…) is not a function](/q/31013221/4642212). – Sebastian Simon Jul 26 '21 at 21:35
  • @Bergi I still don't understand why? They are still two separate functions? Why does the self-invoking one is passed as an argument? How? How is the `myFunc` invoked, and how is the returned `localScope` invoked? – happy_story Jul 26 '21 at 22:08
  • @IloveCoffee Because `let myfunc = function(a) {…}(…)` is still a function call. They are not separate because you're missing semicolons after all your statements. Do not omit semicolons unless you understand how exactly JS deals with them. – Bergi Jul 27 '21 at 12:28

1 Answers1

1

Remove some newlines and you can see why:

let myfunc = function(a) {
     return function localScope(b) {
         console.log(a + b + " this")
     }
 }(function () {
     console.log('test')
  })() 

And function(){}() is actually valid syntax to call that anonymous function. You can use a defensive semicolon to properly split the statements:

let myfunc = function(a) {
     return function localScope(b) {
         console.log(a + b + " this")
     }
 };

(function () {
     console.log('test')
  })() 
Kelvin Schoofs
  • 8,323
  • 1
  • 12
  • 31
  • I still don't understand why? They are still two separate functions? Why does the self-invoking one is passed as an argument? How? How is the `myFunc` invoked, and how is the returned `localScope` invoked? – happy_story Jul 26 '21 at 22:08
  • Because `function myFunc(){} (function test(){}) ()` _(gave the anonymous functions names to explain with)_ calls your `myFunc` function with `function test(){}`, and then calls the result (which is `localScope`) due to that final `()` at the end. That's why your `console.log` prints `undefined this`. `a` is the `function test(){}` while `b` is `undefined` since the `()` doesn't pass anything. – Kelvin Schoofs Jul 26 '21 at 22:11
  • I have never studied this before. Could you please try to elaborate? Yes, I get that the anon self-invoking function is passed as an argument, and then consoled out, and that's why `b` is undefined, but how.. why .. is the `myFunc()` invoked BY the anon self-invoking function? How does that work out? And how exactly is the returned `localFunction()` invoked? Who invokes it? – happy_story Jul 26 '21 at 22:31
  • How does the anon function invokes `myFunc()` by passing itself as an argument.. and then how does it also invoke the returned function as well? – happy_story Jul 26 '21 at 22:33
  • As I mentioned in the answer, `function(){}(args}` defines a function and immediately calls it. It even has its own name, namely [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE). Newlines don't really split statements. E.g. `"some string" \n + "some other string"` is a valid expression over two lines. In your case, you did exactly the `function(){}(args)` I mentioned except instead of `args` you have that `function(){console.log('test')}`. Therefore you're doing: `myfunc = (function(a){...})( function(){...} )()`. The first call returned `localFunction` which the `()` then calls. – Kelvin Schoofs Jul 26 '21 at 22:39
  • I didn't know that `function test() {..}()` could invoke the function! I thought this is just an anon self-invoking syntax. If it works without even wrapping the entire function with brackets `()`, then why do anon self-invoking functions need to be wrapped in brackets? Also, I get how the `myFunc()` is triggered by the below self-invoking function, but once triggered, it returns another function `localScope()`, so who triggers that returned function? How does the self-invoking function triggers both? If I just add `()` after `myFunc()`, it's not triggering both. – happy_story Jul 26 '21 at 22:56
  • Do you mean to say that, because the anonymous self-invoking function has two brackets `( .. )` and again `()`, the first one invokes the `myFunc()`, and the second one invokes the returned `localScope()`? – happy_story Jul 26 '21 at 22:57
  • They don't _have_ to be wrapped in brackets (except if they're arrow functions), but it helps with readability. After all, `function(){}()` can make it easy to misread. And yes, your 2nd comment about how it invokes `localScope` is correct. You're basically doing `myFunc = func1(func2)()` which is the same as `myFunc = ( func1(func2) )()` if you add extra brackets for readability. – Kelvin Schoofs Jul 26 '21 at 22:59