Why I can’t invoke function inside of another function in JS, if functions are objects?

I can invoke function inside the object, but I can’t invoke function inside the function. How it really works?

const baz = {
    bar() { console.log('hi');}
}

baz.bar(); // -> hi

function foo() {
    function bar() {
        console.log('there');
    }
}

console.log(foo.bar); // undefined, why???

Answer

Because in your second example, foo has no bar property on it, but foo.bar looks for a property called bar on the foo object. If your question comes from the fact both of your examples use {}, then: in JavaScript {} are used for a least a couple of different things:

  1. Delimiting an object literal, as in your first example
  2. Delimiting the body of a function, as in your second example, or a block (for instance, the block attached to an if or for or similar)

Those are completely unrelated things, they just look somewhat similar. (There are other things {} are used for, such as indicating named imports [import { x } from "y"], object destructuring [though really that’s just the converse of #1 above], probably others…)

Also note that with your code, a bar function is only created if and when you call foo, and a different bar function is created every time you call foo.

You can assign a function to a property on another function, though, just not that way. For instance:

function foo() {
    console.log("foo");
}
foo.bar = function() {
    console.log("bar");
};
foo.bar(); // "bar"
foo();     // "foo"

That works because, as you said, functions are objects, so you can add properties to them.

If you want a more similar declarative syntax, you can create an object with the functions and copy them to the target function via Object.assign:

// Object.assign copies properties from the second argument onward
// onto the object in the first argument
const foo = Object.assign(
    // Function expression, creates a function:
    function foo() {
        console.log("foo");
    },
    // Object literal, creates an object:
    {
        bar() {
            console.log("bar");
        }
    }
);
foo.bar(); // "bar"
foo();     // "foo"