A fun conversation to understand (arrowFunction).prototype
Divyesh Parmar
Posted on August 11, 2019
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
but
console.log(function * () {}).constructor) // GeneratorFunction () {}
console.log(async function () {}).constructor) // AsyncFunction () {}
console.log(async function * () {}).constructor) // AsyncGeneratorFunction () {}
Arrow functions, on the other hand, are just considered normal functions despite their unique behavior and syntax.
console.log((() => {}).constructor) // Function () {}
Variations of arrow functions also correlate to their respective function type
console.log((async () => {}).constructor) // AsyncFunction () {}
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'
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
}
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
In this sense, arrow functions also have prototypes which refer to their respective function types.
console.log(Object.getPrototypeOf(() => {}) === Function.prototype) // true
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
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
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
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.
Posted on August 11, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.