JavaScript has two types of data: primitive values and objects. Primitive values in JavaScript are: String, Number, Boolean, Symbol, BigInt, Undefined, and Null.
Anything other than the above types are considered objects—even arrays and functions are considered objects. While typeof null === 'object'
, it’s not really considered an object; it indicates the absence of an object.
Primitive values vs. objects
The main difference between primitive values and objects is that you can create complex data structures with objects. With a number type, for example, you can just have a single number, but with objects, you can have multiple fields, each one with a different data type—you can include all primitive types and objects in it.
Another important difference between primitive values and objects is that primitive values are immutable but objects are mutable. This means that once a variable is assigned with a primitive value, you can’t change the value itself; you can only change the variable containing it with a new value by reassigning it—only if it’s declared with let
or var
.
let age = 28
// to change it you need to reassign age
age = 30
On the other hand, if you assign a variable with an object (even with const
), you can change its properties without reassigning the variable that contains it.
const user = { age: 28 }
// you can change user properties directly
user.age = 30
The need for complex data structures
Any typical application will inevitably use objects as a data structure. If you send a request to load some data, the server will return JSON, which will be converted to an object. Another example would be grouping some values into an object to represent some entity in your app—for example, an object that represents a user in your app.
Object literals in JavaScript makes it easy to create objects for your data.
// This is an object
{
name: 'Example Name',
age: 28
}
An object is more than a data structure
Objects are not only for containing data; they can also contain functions to work on that data—these functions represent the behavior of that object.
Objects are the basic building blocks of OOP (object-oriented programming) languages. With objects, you can design your system in a modular way including all the great benefits like encapsulation and polymorphism.
Since JavaScript is considered OOP (and FP as well), then all the great benefits of OOP applies to JavaScript—which means objects are more than a data structure.
Now, I have a question: is it bad if we use objects for holding our data as a key-value data structure?
As with everything, it depends.
When I’m implementing my application with classes and modules, then I would put data in my objects along with the operations that work on that data. These objects can represent many things in the system—they can be the business rules, data-access layers, or they can hold the state of a view.
However, if all I’m trying to have here is a variable that contains a group of values as key-value pairs, then I should not use an object; I should use a record.
What is a record?
A record is like an object except that it cannot have methods, and its values can’t be changed—in other words, it’s an immutable object.
Records can come from a server response or the lines of a file you read, for example.
Records should be treated as values. They are just some kind of data structure that we use to read values.
How to create your records
There’s a proposal for records and tuples that, at the time of this writing, is in stage 2.
Until it’s part of the language, you need to create them with literal objects. Since literal objects are mutable by default, you need to make them immutable with Object.freeze
.
const record = Object.freeze({
name: 'Example Name',
age: 28
})
Where do you find records?
Since the concept of records doesn’t exist yet, you need to distinguish between objects and records based on their source and use.
If, for example, they are were originally a JSON data (like receiving a response from a server), then they are considered records. Another example is the data you send with your requests.
Records can be part of your project’s code. For example, you might define an enum that maps a key to some value.
const SOCIAL_MEDIA = Object.freeze({
TWITTER: 'twitter',
FACEBOOK: 'facebook'
})
Why should you distinguish records from objects
Once you start seeing the difference between them, you will take better decisions when designing your system.
For example, when you view the response data as a record, you will avoid adding behavior to that data and instead implementing your own domain model objects. This way, you will have your business logic separated in a clear way that works well with the rest of your system.
Another reason is that you will avoid bugs that comes from mutability issues. In other words, when you are sure that your records don’t change (because you Object.freeze them), then the usage of that record will be consistent across all parts of your app—you’ll expect to read the same values.