The Anatomy of a JavaScript Function
Functions in JavaScript are like classes to Java. They’re the fundamental modular unit: the cell in life, the note in music, the word in language, the funky chicken in dance. JavaScript functions are full fledged objects, often called first-class objects[1], having properties and methods, mutable values, and dynamic memory. Douglas Crockford, one of the early gurus and longtime critic of the language wrote[2]:
The best thing about JavaScript is its implementation of functions.
In this post I cover the qualities of functions as objects, how function objects are created, the difference between constructor, prototype, and instance objects, how the new
operator works, and useful properties of functions.
First Class Objects #
Functions have the same capabilities as other objects. So it’s no secret they can be:
passed as arguments
function a() { ... }
function b(a) { ... }
b(a);
returned from functions
function a() {
function b() { ... }
return b;
}
assigned as data values
var a = function() { ... };
have properties and methods
function a() { ... }
var obj = {};
a.call(obj);
defined anywhere an expression can be, allowing them to be nested
function a() {
function b() {
function c() {
...
}
c();
}
b();
}
a();
Constructors, Prototypes, & Instances #
Before we look at function internals let’s quickly review the basics of prototypes. Every function carries a prototype property linked to an internal prototype object[3]. When a function is invoked as a constructor to create instance objects, which we’ll cover shortly, those instance objects carry the link to the same prototype object, giving them shared behavior across instances.
The key to understanding functions is in the relationship between three objects:
- the
Function
constructor object - its prototype object
- function instance objects
Let’s focus on the first two. The built-in Function
constructor object is itself a function, having a prototype property that references it’s prototype object thru Function.prototype
[3]. These two objects are built into the JavaScript runtime before any of our code starts executing.
We can visualize them in memory.
Creating Function Instances #
Meet 2 functions.
function Alpha() { ... }
function Bravo() { ... }
The simple function
keyword is a workhorse. When these two functions are defined:
- The
function
keyword implicitly creates a brand new instance object and passes it into constructorFunction()
. - The new instance object is implicitly assigned many internal properties, one being the
[[prototype]]
property. TheFunction
constructor’s prototype property referencing it’s prototype object is copied into this new object’s[[prototype]]
property[3]. - The new instance object is also a function, so it too has a prototype property that refers to a prototype object that is automatically created, allowing the possibility that it will be used as a constructor.
- The new object is returned and assigned to it’s reference.
[3]
Augmenting Prototypes #
We can also define methods on their prototype objects by referencing the constructor function’s prototype
property,
Alpha.prototype.doStuff = function() { ... };
Our Alpha
instance and any object instances created by it have access the prototype’s shared methods.
After defining these two functions and augmenting Alpha.prototype
, we have:
[3]
Note, internal properties are displayed inside double brackets.
The result is two function instance objects, referenced thru Alpha
and Bravo
. They link to the single Function.prototype
thru their [[prototype]]
property, giving them access to the prototype’s useful methods, such as .call()
and .apply()
.
new Operator #
Let’s take this a step further. We invoke function instance object Alpha
with the new
operator, treating it as a constructor function.
var alpha1 = new Alpha();
var alpha2 = new Alpha();
Similar steps as above occur, with some variation,
- A new empty instance object is created and passed into constructor
Alpha
asthis
. - The new instance object is implicitly assigned many internal properties common to all objects, one being the
[[prototype]]
property. TheFunction
constructor’s prototype property referencing it’s prototype object is copied into the new object’s[[prototype]]
property[3]. - Any
this
assignments are made to the new object. - The new object is returned and assigned to it’s reference
alpha1
andalpha2
.
[3]
In short, we used new
to invoke Alpha
as a constructor function, creating two instance objects referenced as alpha1
and alpha2
. The key differences here from when function object Alpha
was created is alpha1
and alpha2
are objects but are not functions, so they don’t have prototype objects.
And our objects in memory now look like:
Function Instance Properties #
We briefly covered how function instance objects contains several internal properties and methods designed for its execution. It’s worthwhile to have a general understanding of these:
Property | Value Description |
---|---|
prototype | Automatically created for every function, providing the possibility that the function will be used as a constructor[3] |
[[Code]] | Function body code between { and }
|
[[FormalParameters]] | Function’s parameters |
[[BoundThis]] |
this parameter referencing calling object |
[[Call]] | Only functions have this property. It’s invoked via () to internally execute function body code |
[[Scope]] | Scope chain. Used for closures. |
[[Class]] | Identifies object’s internal type. Assigned "Function" [5]
|
[[Construct]] | Creates an object. Implemented in functions that can be invoked via the new operator such as all custom functions, and many built-in and host object functions. |
[6][7]
In this post we covered the depths of functions as objects, how function objects are created, what the new
operator does, and internal properties and methods used in their execution.
I’d much enjoy your comments and questions. Feel free to reach out on LinkedIn.
References #
[1] - Resig, J., & Bibeault, B. (2013). Secrets of the JavaScript Ninja (p. 32). Shelter Island, NY: Manning.
[2] Crockford, D. (2008). JavaScript: The good parts (p. 3). Beijing: O'Reilly.
[3] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
[4] - Flanagan, D. (2006). JavaScript: The definitive guide (5th ed., pp. 54-55). Cambridge: O'Reilly.
[5] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3
[6] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5
[7] ECMAScript 5.1 http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.2