DEV Community

Divyesh Parmar
Divyesh Parmar

Posted on

A fun conversation to understand (arrowFunction).prototype

I'm writing this post to document a conversation I had with /u/senocular on reddit. I was asking about if it is disadvantageous to that arrow functions lexically bind "this"?

Later on I got intrigued to know that arrow Function's doesn't have their constructor like how normal functions have in their prototype property.

It sounds like you're trying to ask about one thing, but really could be two...? First:

...if the arrow functions have their own constructor function...

This is a good question. And the answer is no. While arrow functions are functions (obviously), and inherit from the Function type, they do not have their own constructor - at least not one specific to arrow functions. Of the available function constructors, you have:

  • Function

  • GeneratorFunction

  • AsyncFunction

  • AsyncGeneratorFunction

Function is actually the only one thats globally accessible, the others are only accessible through instances of functions of their respective function types as created with the function keyword.

console.log(Function) // Function () {}
console.log(GeneratorFunction) // Error, not defined
console.log(AsyncFunction) // Error, not defined
console.log(AsyncGeneratorFunction) // Error, not defined
Enter fullscreen mode Exit fullscreen mode

but

console.log(function * () {}).constructor) // GeneratorFunction () {}
console.log(async function () {}).constructor) // AsyncFunction () {}
console.log(async function * () {}).constructor) // AsyncGeneratorFunction () {}
Enter fullscreen mode Exit fullscreen mode

Arrow functions, on the other hand, are just considered normal functions despite their unique behavior and syntax.

console.log((() => {}).constructor) // Function () {}
Enter fullscreen mode Exit fullscreen mode

Variations of arrow functions also correlate to their respective function type

console.log((async () => {}).constructor) // AsyncFunction () {}
Enter fullscreen mode Exit fullscreen mode

Note: arrow functions aren't supported for generators though there is a proposal for this in stage 1.

But if you use a function constructor to create a function, it will have normal function behavior, not arrow function behavior.

(() => this).call('value') // global (undefined in strict)
new Function('return this').call('value') // 'value'
Enter fullscreen mode Exit fullscreen mode

You can consider this a limitation of the Function constructor. It simply doesn't support the creation of arrow functions much in the same way it doesn't support being able to create closures.

{
  let outer = 'value'
  new Function('return outer')() // Error, outer not defined
}
Enter fullscreen mode Exit fullscreen mode

Now, all this could be unrelated to

...like how normal fucntions have in their prototypes?

This depends on what you mean by prototypes. Do you mean the prototype of the functions or do you mean the prototype property of the functions?

As objects, functions have prototypes which point to their respective types - what they inherit from, or more specifically the prototype property of their types. For example a normal function instance inherits from Function.prototype. This is where it gets methods like call and apply. We've also seen from above that other function variations have their own function types like GeneratorFunction and AsyncFunction which also have their own prototype properties those functions inherit from.

console.log(Object.getPrototypeOf(function * () {}) === GeneratorFunction.prototype) // true
Enter fullscreen mode Exit fullscreen mode

In this sense, arrow functions also have prototypes which refer to their respective function types.

console.log(Object.getPrototypeOf(() => {}) === Function.prototype) // true
Enter fullscreen mode Exit fullscreen mode

As you can see, this is still Function and not anything unique like ArrowFunction because arrow functions don't have their own specific constructor type.

On the other hand, arrow functions themselves do not have their own prototype property, meaning they can't be used as constructors or represent a type themselves.

const arrowFn = () => {}
console.log(arrowFn.prototype) // undefined
new arrowFn() // Error, not a constructor
Enter fullscreen mode Exit fullscreen mode

In fact most function variations coming out of ES6+ don't have their own prototype properties and cannot be constructors.

const obj = { method () {} }
console.log(obj.method.prototype) // undefined
new obj.method() // Error, not a constructor

const asyncFn = async function () {}
console.log(asyncFn.prototype) // undefined
new asyncFn() // Error, not a constructor
Enter fullscreen mode Exit fullscreen mode

Exceptions are class functions, as they are explicitly meant to represent constructors (but as with object methods above, class methods cannot be constructors), and generator functions which are a result of a bug in the spec that allowed them to be constructors in ES6. This bug was fixed in ES7 and while generators can no longer be constructors, their prototype property was still left behind

const genFn = function * () {}
console.log(genFn.prototype) // Object {}
new genFn() // Error, not a constructor
Enter fullscreen mode Exit fullscreen mode

When it comes down to it, arrow functions are just variations of normal functions. They have the same constructors, but those constructors can't create them, and arrow functions themselves also can't be constructors.

Top comments (0)