Secrets of the JavaScript Object API
Tags: javascript, webdev
Published: 2020-01-15 13:52:26

**This post was taken from [my blog](https://areknawo.com), so be sure to check it out for more up-to-date content.**

The “everything is an object” maxim clearly describes just how important _objects_ are in JavaScript. These structures form the basis of the entire language! With that said, I think it’s easy to assume that the _JS Object API_ doesn’t get the attention it deserves.

Think about the ways you usually interact with objects. And I don’t mean some complex, dedicated ones like functions or arrays (which are still objects after all), but simple structures that you use to organize your data. Certainly, you use _dot_ or _bracket notation_ to access the object's properties, and maybe even [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) or the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) if you write more modern code. But what else do you use? I bet it's not that much.

Because of the fundamental nature of objects, they and their syntax are meant to be simple. But you might find yourself in a situation where you require some more advanced functionality that you don't know, but the Object API can provide. So, in this blog post, I’d like to walk you through some of these lesser-known functionalities, that might actually be really useful!

# Object manipulation

Aside from everything I've just mentioned, Object API provides you with additional methods to interact with your objects. The two I'd like to tell you about here are `Object.defineProperty()` and `Object.defineProperties()`.

## [Object.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)

The most obvious way to set an object's property is by doing it right when declaring the object or later on with the dot or bracket notation. Thus, having the `Object.defineProperty()` method might feel a bit repetitive and unnecessary. And in most cases it is, but it also provides some functionalities that you can't get anywhere else!

`Object.defineProperty()` not only defines/sets/overrides the property's value but its whole _descriptor_ - something like metadata of the property. Take a look:

```javascript
let obj = {};
let firstProperty = 10;

Object.defineProperty(obj, "firstProperty", {
	configurable: true,
	enumerable: true,
	get: () => {
		console.log(`Retrieving the property "firstProperty".`);

		return firstProperty;
	},
	set: newValue => {
		console.log(`Setting the property "firstProperty" to ${newValue}.`);

		firstProperty = newValue;
	}
}); 
Object.defineProperty(obj, "secondProperty", {
	configurable: false,
	enumerable: false,
	writable: false,
	value: "value"
});

obj.firstProperty; // Retrieving the property "firstProperty". 10
obj.secondProperty; // "value"
obj.firstProperty = 20; // Setting the property "firstProperty" to 20.
obj.secondProperty = "secondValue";
obj.firstProperty; // Retrieving the property "firstProperty". 20
obj.secondProperty; // "value"
```

Above I use `Object.defineProperty()` to set two properties on the object `obj`. The first argument that the method takes is the _object_ that the property will be set on. It'll be returned later on from the `Object.defineProperty()` call. After that comes the second argument, which is the _property's name_ and the last one, which is the _property's descriptor_.

I used two properties on purpose - to showcase the two flavors of descriptors - the _data_ and the _accessor_ ones. They share two properties - `configurable` and `enumerable`. The first one defines whether the property's descriptor type can be changed (e.g. by calling `Object.defineProperty()` the second time) or the property deleted (with the `delete` keyword). while the second one controls whether the property appears in the `for... in` loop or when used with some methods we'll discuss later on. Both properties default to `false`, which sets apart the most basic `Object.defineProperty()` call from the usual syntax.

Now, data descriptors allow you to set two other properties - `value` and `writable`. While the meaning of the first one is obvious, the second one refers to the possibility of changing (aka writing to) the property. Mind you that it's not the same as the `configurable` property, but like the one mentioned, defaults to `false`.

The second kind of descriptors - accessor descriptors, also provide you with two additional properties, but this time they're called `get` and `set`. These should have a form of individual functions that are called accordingly when the property is retrieved and set. They're the same [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) and [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) that you might have seen before, with the difference being that they're defined after the object is created. Just to remind you:

```javascript
let firstProperty = 10;
let obj = {
	get firstProperty() {
		console.log(`Retrieving the property "firstProperty".`);

	return firstProperty;
	},
	set firstProperty(newValue) {
		console.log(`Setting the property "firstProperty" to ${newValue}.`);

		firstProperty = newValue;
	}
};
```

Properties that have setters and/or getters cannot have values of their own. Instead, they either calculate them from the other properties or use different variables.

## [Object.defineProperties()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties)

So, if you want to use `Object.defineProperty()` to define multiple properties, you'll be better off using `Object.defineProperties()` instead. Here's how it looks:

```javascript
let obj = {};
let firstProperty = 10;

Object.defineProperties(obj, {
	firstProperty: {
		configurable: true,
		enumerable: true,
		get: () => {
		console.log(`Retrieving the property "firstProperty".`);
		
		return firstProperty;
	},
		set: newValue => {
			console.log(`Setting the property "firstProperty" to ${newValue}.`);
		
			firstProperty = newValue;
		}
	},
	secondProperty: {
		configurable: false,
		enumerable: false,
		writable: false,
		value: "value"
	}
});
```

Basically, you just swap out the string argument for an object with property name - descriptor key-value structure, that's easier to use, read and manage when multiple properties are involved.

# Immutability

The introduction of the `const` keyword in ES6 as a new way of declaring "variables" spread a little controversy as to what exactly is _constant_. As it turns out - it's not the value (as usual), but the _variable_ itself. So, if you e.g. assign an object to such a variable, you won't be able to change the variable's value, but you'll be able to freely change the properties of the assigned object.

```javascript
const obj = {};

obj.property = 1;
obj.property; // 1
obj = {}; // ERROR
```

This might be OK for most, but not so for the ones striving for _immutability_. `const` doesn't guarantee your variable's value to stay the same unless it's a _primitive_ (i.e. number, string or boolean). And that's where the Object API comes into play, with a set of methods that allow you to define the mutation rules of not only a single property (like with `Object.defineProperty()`) but the entire objects!

## [Object.preventExtensions()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions)

Starting with the most "loose" of the methods, `Object.preventExtensions()` simply prevents any new properties from being added to an object (aka _extending_ it). Once you call it with your object as an argument, no new property will be allowed to be defined (even with the use of `Object.defineProperty()`).

```javascript
const obj = Object.preventExtensions({
	firstProperty: 10,
	secondProperty: 20
});

obj.firstProperty = 100;
delete obj.secondProperty; // true
obj.thirdProperty = 30; // nothing or ERROR
obj.firstProperty; // 100
obj.secondProperty; // undefined
obj.thirdProperty; // undefined
```

`Object.preventExtensions()`, as well as all the other "locking" methods of Object API, return the passed object, making for a nice, immutability-friendly syntax you see above.

Again, after calling the method, you can do pretty much everything but define new properties. This includes deleting and changing the already present property values and descriptors. An attempt to set new property will either be left silent or throw an error (e.g. when you're in strict mode).

You can check whether the object can be extended with the [`Object.isExtensible()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) method.

```javascript
const firstObject = { property: 10 };
const secondObject = Object.preventExtensions({ property: 20 });

Object.isExtensible(firstObject); // true
Object.isExtensible(secondObject); // false
```

## [Object.seal()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal)

If you want to go a bit further than `Object.preventExtensions()`, you can use `Object.seal()` to not only disallow any new properties to be set, but also make all exiting properties _non-configurable_. Remember the `configurable` property from the `Object.defineProperty()`? `Object.seal()` is like combining `Object.preventExtensions()` with `Object.defineProperties()` where you override all existing properties to be non-configurable. Your properties are still _writable_ (unless you've previously set them not to), so you can easily change their values. However, you cannot delete a property or change the descriptor type (from data to accessor or vice versa).

```javascript
const obj = Object.seal({
	firstProperty: 10,
	secondProperty: 20
});

obj.firstProperty = 100;
delete obj.secondProperty; // false
obj.thirdProperty = 30; // nothing or ERROR
obj.firstProperty; // 100
obj.secondProperty; // 20
obj.thirdProperty; // undefined
```

If you want to check whether the given object has already been sealed, you can use the [`Object.isSealed()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) method. Also useful might be the previously-discussed `Object.isExtensible()` method, which, when the object is sealed, will return `false`.

```javascript
const obj = Object.seal({ property: 20 });

Object.isSealed(obj); // true
Object.isExtensible(obj); // false
```

## [Object.freeze()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)

Lastly, if you want to take the immutability of your objects to another level, `Object.freeze()` is at your disposal. As the name indicates, it not only makes your object _non-extensible_ and _non-configurable_ but also completely _unchangeable_. You can only access your previously-defined properties and that's it! Any attempt to change anything won't work and will either be left silent or throw an error.

```javascript
const obj = Object.freeze({
	firstProperty: 10,
	secondProperty: 20
});

obj.firstProperty = 100; // nothing or ERROR
delete obj.secondProperty; // false
obj.thirdProperty = 30; // nothing or ERROR
obj.firstProperty; // 10
obj.secondProperty; // 20
obj.thirdProperty; // undefined
```

To check if an object is "frozen", you'll have to use the [`Object.isFrozen()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) method, but keep in mind that both `Object.isExtensible()` and `Object.isSealed()` still apply.

```javascript
const obj = Object.freeze({ property: 20 });

Object.isFrozen(obj);
Object.isSealed(obj); // true
Object.isExtensible(obj); // false
```

Now, just to remind you that as "everything is an object", the same "locking" methods can be applied to all the other objects that are present in JS. Examples of such include custom _classes_, _functions_, and most importantly - _arrays_. This is especially great when you're going for full-blown immutability and _functional programming_ in pure JS.

```javascript
const arr = Object.freeze([1, 2, 3]);

arr.push(4); // ERROR
arr.pop(); // ERROR
arr[0] = 0; // nothing or ERROR
```

# Iteration

As we're on the topic of arrays, let's talk about _iteration_. Looping through arrays is normal, but what about objects? There's certainly less freedom in that department.

There's a [`for...in`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) loop that lets you iterate through _enumerable_ properties (remember the descriptors we've talked about before) of an object and read their key names.

```javascript
const obj = { 
	firstProperty: 10,
	secondProperty: 20
};

for (const key in obj) {
	const value = obj[key];
}
```

However, this method is pretty limiting. You only get access to the property keys and you have to use that to access the value if you need that. That's one additional (and possibly unnecessary) _Line Of Code_ (LOC) to be added to your codebase - one that could have been avoided.

Basically, you have a lot less _flexibility_ with simple objects than with arrays and their API. So, how about converting objects to arrays and looping through that instead? Well, that's exactly what some of the Object API methods allow you to do!

## [Object.keys()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)

Let's start with the simplest of methods - `Object.keys()`. Like the name implies it returns all the keys of the passed object in a form of an _array of strings_. When your data is organized in such a way, you can use e.g. the [`.forEach()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) method from Array API to loop through all of the retrieved property keys.

```javascript
const obj = { 
	firstProperty: 10,
	secondProperty: 20
};
const keys = Object.keys(obj); // ["firstProperty", "secondProperty"]

keys.forEach(key => { 
	const value = obj[key];
});
```

Still, `Object.keys()` isn't that of a compelling option. It pretty much gives you the same result as the `for...in` loop at the loss of some performance. However, if you consider this syntax better or cleaner you shouldn't care about such small performance benefits.

`Object.keys()` also stands out from the rest of related Object API methods, with better support for older browsers. Most notably it supports up (or rather down) to _IE 9_, while the next two methods don't support this particular browser at all! Still, if the support of old browsers matters to you and you don't want to use any polyfills, you'll be better served by the `for...in` loop, which supports even _IE 6_!

## [Object.values()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values)

As for the "need only the values" case we've discussed before, `Object.values()` will serve this purpose just fine. Instead of keys, it returns an _array_ of object's _property values_.

```javascript
const obj = { 
	firstProperty: 10,
	secondProperty: 20
};
const values = Object.values(obj); // [10, 20]

values.forEach(value => { 
	// do something with value
});
```

## [Object.entries()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)

Finally, `Object.entries()` is a method that gives you access both to the object's keys as well as its values. It returns them in the form of an _array of key-value pairs_ (arrays).

```javascript
const obj = {
	firstProperty: 10,
	secondProperty: 20
};
const entries = Object.entries(obj); // [["firstProperty", 10], ["secondProperty", 20]]

entries.forEach(([key, value]) => { 
	// do something with the key and the value
});
```

`Object.entries()` feels especially good when used with the [destructuring syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) like in the example above.

## [Object.fromEntries()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries)

While `Object.fromEntries()` isn't a method meant for iterating through objects, it does basically the opposite of what the `Object.entries()` method does. Namely, it converts an array of key-value pairs (`Object.entries()` output) to an _object_. Just a fun fact!

```javascript
const obj = { 
	firstProperty: 10,
	secondProperty: 20
};
const entries = Object.entries(obj); // [["firstProperty", 10], ["secondProperty", 20]]
const objCopy = Object.fromEntries(entries);
```

## Aren't Maps better?

In comparison to the `for...in` loop, any of these methods don't take into consideration properties from the object's _prototype_. To achieve the same (usually desired) effect with the `for...in` loop, you'll have to use the [`.hasOwnProperty()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) method to check whether the property is the object's own.

You should also remember that both `for...in` loop and Object API methods ignore the _non-enumerable_ properties (like I've said before), and the ones that use [Symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) as their keys.

In reality, though, all that is kind-of "low-level" (as far as the JS goes) stuff, and you're unlikely to have to deal with any of such issues in real-world projects. What's more important, however, is the fact that any of the ways of object iterations we've just covered don't guarantee the _order_ of iterated keys, values, entries or whatever. It usually follows the order in which the properties were defined, but it's not a good practice to follow such an assumption.

If you're going for something that's like an array and object combined, you might be interested in [Maps](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). These are structures that organize data in a _key-value_ fashion and allow for _iteration_ while maintaining the _correct order_ of the key-value pairs. They also have decent [cross-browser support](https://caniuse.com/#feat=mdn-javascript_builtins_map) and other unique properties known from both arrays and objects. I've covered them already in one of [my previous posts](https://areknawo.com/rocking-js-data-structures/), so go check it out if you're interested!

# Final words

That’s it! Hope you enjoyed the article and learned something new. The amount of _possibilities_ JS API can offer is truly impressive! Here, we've barely scratched the surface! From the Object API itself, we've missed some more complex, _prototype-related_ methods. I think they're not as useful as the ones listed (especially when writing modern JS) but I encourage you to explore them on your own to strengthen your JS knowledge!

So, if you like the article, consider _sharing it_ with others and _following me_ on [Twitter](https://twitter.com/areknawo), [Facebook](https://facebook.com/areknawoblog) or through [my personal blog](https://areknawo.com) for more up-to-date content. You can also check out my [YouTube channel](https://www.youtube.com/channel/UCUw6bg379ONlG1E_oxhwi-A/) and drop a like or _a sub_ there. As always, thanks for reading and have a great day!