Skip to content

Instantly share code, notes, and snippets.

@bookercodes
Last active June 12, 2016 12:28
Show Gist options
  • Save bookercodes/56d7433adffb9cb0240c917a0b16e68d to your computer and use it in GitHub Desktop.
Save bookercodes/56d7433adffb9cb0240c917a0b16e68d to your computer and use it in GitHub Desktop.

Micro Post: Sneaky Arrow Functions

I was just hacking away on a side project when I wrote this code:

const screencastMock = {
  findOne: () => { }
};

My intention was to make findOne return an empty object literal ({}) but it actually returns undefined.

Can you spot my mistake?


Answer

When JavaScript sees () => { }, it interperts the { } part as a block statement instead of an empty object literal:

const screencastMock = {
  findOne: () => { var insideABlockStatement = true; var owned = true; } // valid JavaScript!
};

The block statement doesn't contain a return statement so this is implicitly returned. In this context, this was undefined, hence why findOne returns undefined.

To successfully return an empty object literal, I should have surrounded the empty object literal in parentheses, like this:

const screencastMock = {
  findOne: () => ({})
}

I found this error amusing because I'm well aware of this caveat with arrow functions and yet it still tripped me up 😆! If I wasn't aware of this caveat, who knows how long I could have been stuck here...

I wounder if there's an ESLint plugin available to help avoid this mistake...?

@RickWong
Copy link

How about this style suggestion: always prefer the object method notation. It requires explicit return-statements, and it leaves this alone.

const screencastMock = {
  findOne () {
    return {};
  }
};

@imrvelj
Copy link

imrvelj commented Apr 15, 2016

I don't really get what's the big deal here.
You wrote: function() {} and expected an object literal in return? Ookay.

But thanks for the ({}) tip, I'll probably never use it but it's a cool thing to know.

@opcodewriter
Copy link

opcodewriter commented Jun 12, 2016

If {} is interpreted as a block in your example, why isn't it interpreted here as a block too:
console.log({});
It prints Object {} not undefined

@opcodewriter
Copy link

opcodewriter commented Jun 12, 2016

I will answer my own question:
I guess it's because arrow functions expect either an expression or a block.
A fat arrow only knows about block, it doesn't know about object literal. So if you use { } it always treats as a block.
http://exploringjs.com/es6/ch_arrow-functions.html#_arrow-function-syntax

The specs call it "ConciseBody", it's either an expression or function body:
http://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions

ArrowFunction[In, Yield] :
ArrowParameters[?Yield] [no LineTerminator here] => ConciseBody[?In]

ConciseBody[In] :
[lookahead ≠ { ] AssignmentExpression[?In]
{ FunctionBody }

One thing I don't understand in the spec is what AssignmentExpression is.
Why is it called assignment expression? Since we can do this:
const f = x => x;
in this case the body of the arrow function is just x, it's not an assignment expression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment