Asleepace
Asleepace

Reputation: 3745

What does the empty function body return in JS?

Does an empty function body implicitly return undefined in JS?

console.log((() => {})())

Clearly it does right? The console outputs undefined in the example above...

// but let's do something crazy

console.log(((undefined) => undefined)("defined"));

console.log((() => {
  var undefined = "defined";
  return undefined; 
})());

Ok so we can redeclare undefined locally (or globally without strict mode)

console.log(((undefined) => {
  var undefined = "defined_again";
})("defined"));

But the output is still undefined, so it seems it's not implicitly returning undefined, but instead treating the entire expression as void(expr).

This also seems to be the case in TypeScript:

const result = (() => {})()
//    ^?: const result: void

So if the empty function body does implicitly return undefined as outlined in the spec, then why does JS ignore lexical scope for undefined if it's redeclared in the function body?

Obviously at the end of the day the result is undefined, but I'm more curious about the actual implementation behavior of what is implicitly returned?

Does the empty function body actually return something?

Upvotes: 0

Views: 74

Answers (3)

Sergey A Kryukov
Sergey A Kryukov

Reputation: 916

I hope your snipped #2 is already clear. You introduce the variable undefined, and it does not reference the global read-only property undefined. This is just the local variable stored on the stack. As you explicitly return it, the outer stack receives the value 'defined', and the variable itself is removed in the sense of popping the stack.

It is important to understand that undefined is not a global variable or something. There is no such thing. There is the global object globalThis, everything else can be a property of this object. Of course, this object depends on the environment. For example, in a Web browser, it is referentially the same as window.

Anyway,

console.log(globalThis.undefined === undefined);

outputs true, this is the same thing.

The property globalThis.undefined returns the unique object, its value has a unique primitive type. Importantly, you cannot get information on this type by getting its constructor (undefined.constructor will throw an exception): undefined cannot by dereferenced. However, it is not null but a distinctly different object. Note that both expressions myObject == null and myObject == undefined return true if myObject is undefined or null, and this is a very convenient thing for practical purposes. And yet, undefined is not null, we can see it if we use the operator ===. The distinction between null and undefined is very specific to JavaScript.

Now, all of the above should give you an idea of what happens in snippet #3 with its var undefined = "defined_again". Still undefined, you say? But what is undefined? The variable referencing "defined_again" is, again, a stack variable, it does not exist after return, and it is totally unrelated to globalThis.undefined. I hope it should be clear.

The remaining problem is to explain why all functions that don't return anything behave exactly as if they returned globalThis.undefined. You know, I am not looking at JavaScript implementation and am not sure. I just think the particular mechanism is not so important, and it even may vary. What is important is the apparent functionality. In essence, you are right, this is like the void. Maybe the compilation of the source code injects the return of globalThis.undefined in every function, or maybe the void condition is somehow detected at the level of the calling function. If the returned object is not discarded, it becomes accessible in the calling stack frame and found to reference globalThis.undefined, and nothing else. And this is all that matters.

const a = () => { };
console.log(a() === globalThis.undefined);

outputs true.

Upvotes: 1

PMontgomery
PMontgomery

Reputation: 424

None of these are actually logging the empty function body.

Code 1 console.log((() => {})()) is taking an empty function, executing it, and sending its results (undefined) to console.log. The same as if you console.log(undefined).

Code 2.A is passing the execution of a function that takes a parameter and returns that parameter the same as console.log((foo) => foo)("defined")). Having a weird variable name doesn't matter

Code 2.B is really just the same as console.log((()=> "defined")()). Having a weird variable name still doesn't matter.

Code 3. Essentially the same as 1. You aren't returning anything so console.log is returning undefined because that is exactly the result of the function execution.

Upvotes: 0

user2357112
user2357112

Reputation: 281748

It returns undefined, as in the specific ECMAScript language value undefined, not performing a variable lookup for an undefined variable and returning whatever the lookup returns. If you want to go to the spec, you can see that the rule for a function completing normally without an explicit return is

  1. Return ReturnCompletion(undefined).

where a bold undefined in variable-width font means the ECMAscript language value:

In this specification, ECMAScript language values are displayed in bold. Examples include null, true, or "hello". These are distinguished from ECMAScript source text such as Function.prototype.apply or let n = 42;.

(And yeah, it's a little awkward that ECMAScript source text is also bold.)

Upvotes: 2

Related Questions