Function is an Object?
If you regularly read articles about Javascript, you will probably see people often say: "Oh function is actually an object". But wait, If it is like that then:
- Why
typeof
offunction
is return "function" instead of "object"? - How we can make a function call on an object?
In this article, we're going to "investigate" whether that's the case and clear up misunderstandings about functions.
1. Does typeof
return the data type?
Firstly, we need to know about list of data type in Javascript:
- undefined
- null
- boolean
- string
- symbol
- number
- bigint
- Object
Learn more at: ECMAScript Specification
As you see, there is no one called 'function'. But let's run this code below:
function foo() {}
console.log( typeof foo); // -> 'function' - WHAT?
So are we wrong or does typeof really have a "problem" 🤔?
The typeof
Operator
To understand how this function works, we need to search the ECMAScript specification document at Section 13.5.3. There are the steps of typeof
function but just focus on step 5th:
5. Return a String according to Table 41.
Looking at the last row, if an Object already has a Call
method implemented then return "function". Instead of returning the left column (Type of val), typeof
returns a string value in the right column. So we can say, typeof
does not implicitly return the type.
At this point, we have a clue that the function is actually an object. But this Object is so strange 😬
2. Function object
Then what is Call
method?. According ECMAScript specification, function object
contains two internal methods: Call
and Construct
. The Call
method must be implemented. Note that internal methods are only called in the Javascript engine, they are not a method of an object. In particular we cannot view them via "."
, for example foo.construct
.
- Call: Executes code associated with this object. Invoked via a function call expression.
- Construct: Creates an object. Invoked via the new operator or a super call.
So to summarize, we can say: an object is considered a function object only if it implements the Call
method. Every object that supports Construct
must support Call
.
3. Function Definitions
So how does Javascript create a function object when we declare it?
Function Declaration
function foo() {
return 0;
}
The above is a common way of declaring a function. Now let's see how the Javascript engine works.
Firstly, we divide the above statement into small parts according to the following pattern:
function Name ( Parameters ) { FunctionBody }
There are steps that Javascript Engine process:
(I have omitted the steps that are not in the scope of the article)
- Let
name
be string value of Name. - Let
F
beOrdinaryFunctionCreate(Function.prototype, Parameters, FunctionBody)
. - Perform
SetFunctionName(F, name)
. - Perform
MakeConstructor(F)
. - Return
F
.
It has three functions we need to consider OrdinaryFunctionCreate
,SetFunctionName
,MakeConstructor
and at the end it returns an function object F
.
Note: Those functions are internal abstract function, Javascript need to implement them in its engine. We can't call them in our code as well.
OrdinaryFunctionCreate Function
It is used to specify the runtime creation of a new function with a default Call
internal method and no Construct
internal method. It returns a function object
.
There are steps:
(I have omitted the steps that are not in the scope of the article)
- Let
F
beOrdinaryObjectCreate(Function.prototype)
- Set
F.[[Call]]
implement. - Return
F
We found a new function OrdinaryObjectCreate
, It is used to specify the runtime creation of new ordinary objects, it also set Function.prototype
into F
. And you see, in step 2, Javascript Engine already implement the Call
method.
At this point, we know how Javascript assigns the Call
method.
SetFunctionName Function
It adds a "name" property to F
.
function foo() {}
console.log( foo.name ); // -> "foo"
MakeConstructor Function
It creates a constructor. Invoked via the new
operator or a super
call. But a function object is not necessarily a constructor and such non-constructor function objects do not have a Construct
internal method.
4. Function Call
Function call is one of Left-Hand-Side Expressions. Javascript engine will invoke EvaluateCall
function (It is internal function) with the foo
function as a parameter func
. EvaluateCall
run following this steps:
- If
Type(func)
is not Object, throw a TypeError exception. - If
IsCallable(func)
is false, throw a TypeError exception. - Return ?
Call(func)
.
At step 1, if
statement will check type of func
is object or not.
"hello"()
//-> Uncaught TypeError: "hello" is not a function
At step 2, IsCallable
(internal function) will check Call
already implemented or not.
let obj = {};
obj()
//-> Uncaught TypeError: obj is not a function
Finally, the Call()
function will be called, which will call the Call
method inside the function as we mentioned in OrdinaryFunctionCreate Function
.
At this point, we can understand that even though a function is an object, but an object cannot be called as a function.
Top comments (2)
If you don't know, the main reason to have functions be objects is to support this business:
Once you support lexical closure—allowing inner functions to capture variables from an outer scope—functions have to be objects, because the captured variables have to be stored somewhere.
Wow great, I don't know this one. Thanks for your sharing