Functions are Objects too


In javascript almost everything is an Object - including Functions! This lets you do some interesting tricks that you may not have realised is possible.

Syntax

Just like any other object, you can define properties against any key - the only real difference is that the execution syntax against a function will work.

const myObject = {};
const myFunction = () => {};

myObject.property = "This is Valid";
myFunction.property = "This is also Valid";

myFunction(); // This invokes `myFunction`
myObject(); // This throws an error as myObject is not a function

It can be a bit unintuitive to think of functions this way and it may not be a great thing to do in your code, but it is good to know that it is possible as you may run into it at some point.

So whats the point?

I am not sure there are a lot of usages of this and it may be quite confusing, but there is one usage I think it quite nice. It all comes down to data retrieval. Lets make some example schema to show what I am talking about.

User
PK id
name
Address
FK user
street
postcode

Here we have a very basic and perhaps naive user schema. It has a few properties, and its primary key is used an identifier for the Addresses table. The concept being that a user can have multiple addresses.

When viewing an address in code, you will likely retrieve the userId as part of the address object and will then have to call another function to get the user details. Something like:

// { userId: 5, street: "123 Street", postcode: "12345" }
const address = await getAddressById(1);
address.user; // This is the users ID

// You could fetch the user like so
const user = await getUserById(address.user);

But this isn’t the only way.

The secret sauce

Instead of adding the user to the address as a property, we can treat it as if it was a sparse user object already and access the user id through address.user.id.

This opens up a new possibility to add a retrieval of the user object by calling address.user() as a function.

// { userId: 5, street: "123 Street", postcode: "12345" }
const address = await getAddressById(1);
address.user.id; // This is the users ID

// You could fetch the user like so
await address.user();

// Now the user object is populated
console.log(address.user.name); // "Ben Knight"

What is defined here is a way to allow both the quick retrieval of a user, as well as giving the address object the ability to drill into the user details when needed.

Would I actually use this?

The short answer is no, I wouldn’t. Perhaps if you were building an ORM or database library you may want to use this pattern to allow easy retrieval of related objects, but in day to day code I would avoid it. It is a bit too magical for my liking and could lead to confusion for anyone reading the code later on.

But it is always worth knowing and playing with what is possible in the languages you use. You never know when it may come in handy!



Want to read more? Check out more posts below!