Objects in Java Script
Objects in Java Script
Buy EPUB/PDF 👤
Objects
As we know from the chapter Data types, there are seven language types in JavaScript. Six of them are called “primitive”, because
their values contain only a single thing (be it a string or a number or whatever).
In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate
almost every aspect of the language. So we must understand them first before going in-depth anywhere else.
An object can be created with figure brackets {…} with an optional list of properties. A property is a “key: value” pair, where key
is a string (also called a “property name”), and value can be anything.
We can imagine an object as a cabinet with signed files. Every piece of data is stored in its file by the key. It’s easy to find a file by
its name or add/remove a file.
An empty object (“empty cabinet”) can be created using one of two syntaxes:
Usually, the figure brackets {...} are used. That declaration is called an object literal.
A property has a key (also known as “name” or “identifier”) before the colon ":" and a value to the right of it.
1. The first property has the name "name" and the value "John" .
2. The second one has the name "age" and the value 30 .
The resulting user object can be imagined as a cabinet with two signed files labeled “name” and “age”.
1 user.isAdmin = true;
To remove a property, we can use delete operator:
1 delete user.age;
We can also use multiword property names, but then they must be quoted:
1 let user = {
2 name: "John",
3 age: 30,
4 "likes birds": true // multiword property name must be quoted
5 };
Trailing comma
The last property in the list may end with a comma:
1 let user = {
2 name: "John",
3 age: 30,
4 }
That is called a “trailing” or “hanging” comma. Makes it easier to add/remove/move around properties, because all lines
become alike.
Square brackets
For multiword properties, the dot access doesn’t work:
That’s because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations.
There’s an alternative “square bracket notation” that works with any string:
Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do).
Square brackets also provide a way to obtain the property name as the result of any expression – as opposed to a literal string –
like from a variable as follows:
Here, the variable key may be calculated at run-time or depend on the user input. And then we use it to access the property.
That gives us a great deal of flexibility. The dot notation cannot be used in a similar way.
For instance:
1 let user = { ✍
2 name: "John",
3 age: 30
4 };
5
6 let key = prompt("What do you want to know about the user?", "name");
7
8 // access by variable
9 alert( user[key] ); // John (if enter "name")
Computed properties
We can use square brackets in an object literal. That’s called computed properties.
For instance:
The meaning of a computed property is simple: [fruit] means that the property name should be taken from fruit .
Square brackets are much more powerful than the dot notation. They allow any property names and variables. But they are also
more cumbersome to write.
So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then
we switch to square brackets.
Reserved words are allowed as property names
A variable cannot have a name equal to one of language-reserved words like “for”, “let”, “return” etc.
But for an object property, there’s no such restriction. Any name is fine:
1 let obj = { ✍
2 for: 1,
3 let: 2,
4 return: 3
5 }
6
7 alert( obj.for + obj.let + obj.return ); // 6
Basically, any name is allowed, but there’s a special one: "__proto__" that gets special treatment for historical reasons. For
instance, we can’t set it to a non-object value:
That can become a source of bugs and even vulnerabilies if we intent to store arbitrary key-value pairs in an object, and
allow a visitor to specify the keys.
In that case the visitor may choose “proto” as the key, and the assignment logic will be ruined (as shown above).
There exist a way to make objects treat __proto__ as a regular property, we’ll cover it later, but first we need to know more
about objects to understand it. There’s also another data structure Map, that we’ll learn in the chapter Map, Set, WeakMap
and WeakSet, which supports arbitrary keys.
For instance:
In the example above, properties have the same names as variables. The use-case of making a property from a variable is so
common, that there’s a special property value shorthand to make it shorter.
We can use both normal properties and shorthands in the same object:
1 let user = {
2 name, // same as name:name
3 age: 30
4 };
Existence check
A notable objects feature is that it’s possible to access any property. There will be no error if the property doesn’t exist! Accessing
a non-existing property just returns undefined . It provides a very common way to test whether the property exists – to get it
and compare vs undefined:
There also exists a special operator "in" to check for the existence of a property.
For instance:
Please note that on the left side of in there must be a property name. That’s usually a quoted string.
If we omit quotes, that would mean a variable containing the actual name to be tested. For instance:
1 let obj = { ✍
2 test: undefined
3 };
4
5 alert( obj.test ); // it's undefined, so - no such property?
6
7 alert( "test" in obj ); // true, the property does exist!
In the code above, the property obj.test technically exists. So the in operator works right.
Situations like this happen very rarely, because undefined is usually not assigned. We mostly use null for “unknown” or
“empty” values. So the in operator is an exotic guest in the code.
The syntax:
1 for(key in object) {
2 // executes the body for each key among object properties
3 }
1 let user = { ✍
2 name: "John",
3 age: 30,
4 isAdmin: true
5 };
6
7 for(let key in user) {
8 // keys
9 alert( key ); // name, age, isAdmin
10 // values for the keys
11 alert( user[key] ); // John, 30, true
12 }
Note that all “for” constructs allow us to declare the looping variable inside the loop, like let key here.
Also, we could use another variable name here instead of key . For instance, "for(let prop in obj)" is also widely used.
Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order that they are added in
it? Can we rely on it?
The short answer is: “ordered in a special fashion”: integer properties are sorted, others appear in creation order. The details
follow.
1 let codes = { ✍
2 "49": "Germany",
3 "41": "Switzerland",
4 "44": "Great Britain",
5 // ..,
6 "1": "USA"
7 };
8
9 for(let code in codes) {
10 alert(code); // 1, 41, 44, 49
11 }
The object may be used to suggest a list of options to the user. If we’re making a site mainly for German audience then we
probably want 49 to be the first.
The phone codes go in the ascending sorted order, because they are integers. So we see 1, 41, 44, 49 .
So, “49” is an integer property name, because when it’s transformed to an integer number and back, it’s still the same. But
“+49” and “1.2” are not:
…On the other hand, if the keys are non-integer, then they are listed in the creation order, for instance:
1 let user = { ✍
2 name: "John",
3 surname: "Smith"
4 };
5 user.age = 25; // add one more
6
7 // non-integer properties are listed in the creation order
8 for (let prop in user) {
9 alert( prop ); // name, surname, age
10 }
So, to fix the issue with the phone codes, we can “cheat” by making the codes non-integer. Adding a plus "+" sign before each
code is enough.
Like this:
1 let codes = { ✍
2 "+49": "Germany",
3 "+41": "Switzerland",
4 "+44": "Great Britain",
5 // ..,
6 "+1": "USA"
7 };
8
9 for(let code in codes) {
10 alert( +code ); // 49, 41, 44, 1
11 }
Copying by reference
One of the fundamental differences of objects vs primitives is that they are stored and copied “by reference”.
Primitive values: strings, numbers, booleans – are assigned/copied “as a whole value”.
For instance:
As a result we have two independent variables, each one is storing the string "Hello!" .
1 let user = {
2 name: "John"
3 };
Here, the object is stored somewhere in memory. And the variable user has a “reference” to it.
When an object variable is copied – the reference is copied, the object is not duplicated.
If we imagine an object as a cabinet, then a variable is a key to it. Copying a variable duplicates the key, but not the cabinet itself.
For instance:
Now we have two variables, each one with the reference to the same object:
We can use any variable to access the cabinet and modify its contents:
The example above demonstrates that there is only one object. Like if we had a cabinet with two keys and used one of them
( admin ) to get into it. Then, if we later use the other key ( user ) we would see changes.
Comparison by reference
The equality == and strict equality === operators for objects work exactly the same.
Two objects are equal only if they are the same object.
For instance, two variables reference the same object, they are equal:
1 let a = {}; ✍
2 let b = a; // copy the reference
3
4 alert( a == b ); // true, both variables reference the same object
5 alert( a === b ); // true
And here two independent objects are not equal, even though both are empty:
1 let a = {}; ✍
2 let b = {}; // two independent objects
3
4 alert( a == b ); // false
For comparisons like obj1 > obj2 or for a comparison against a primitive obj == 5 , objects are converted to primitives. We’ll
study how object conversions work very soon, but to say the truth, such comparisons are necessary very rarely and usually are a
result of a coding mistake.
Const object
For instance:
1 const user = { ✍
2 name: "John"
3 };
4
5 user.age = 25; // (*)
6
7 alert(user.age); // 25
It might seem that the line (*) would cause an error, but no, there’s totally no problem. That’s because const fixes the value
of user itself. And here user stores the reference to the same object all the time. The line (*) goes inside the object, it
doesn’t reassign user .
The const would give an error if we try to set user to something else, for instance:
1 const user = { ✍
2 name: "John"
3 };
4
5 // Error (can't reassign user)
6 user = {
7 name: "Pete"
8 };
…But what if we want to make constant object properties? So that user.age = 25 would give an error. That’s possible too.
We’ll cover it in the chapter Property flags and descriptors.
That’s also doable, but a little bit more difficult, because there’s no built-in method for that in JavaScript. Actually, that’s rarely
needed. Copying by reference is good most of the time.
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its
properties and copying them on the primitive level.
Like this:
1 let user = { ✍
2 name: "John",
3 age: 30
4 };
5
6 let clone = {}; // the new empty object
7
8 // let's copy all user properties into it
9 for (let key in user) {
10 clone[key] = user[key];
11 }
12
13 // now clone is a fully independant clone
14 clone.name = "Pete"; // changed the data in it
15
16 alert( user.name ); // still John in the original object
● Arguments dest , and src1, ..., srcN (can be as many as needed) are objects.
● It copies the properties of all objects src1, ..., srcN into dest . In other words, properties of all arguments starting from
the 2nd are copied into the 1st. Then it returns dest .
We also can use Object.assign to replace the loop for simple cloning:
1 let user = {
2 name: "John",
3 age: 30
4 };
5
6 let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.
Until now we assumed that all properties of user are primitive. But properties can be references to other objects. What to do
with them?
Like this:
1 let user = { ✍
2 name: "John",
3 sizes: {
4 height: 182,
5 width: 50
6 }
7 };
8
9 alert( user.sizes.height ); // 182
Now it’s not enough to copy clone.sizes = user.sizes , because the user.sizes is an object, it will be copied by
reference. So clone and user will share the same sizes:
Like this:
1 let user = { ✍
2 name: "John",
3 sizes: {
4 height: 182,
5 width: 50
6 }
7 };
8
9 let clone = Object.assign({}, user);
10
11 alert( user.sizes === clone.sizes ); // true, same object
12
13 // user and clone share sizes
14 user.sizes.width++; // change a property from one place
15 alert(clone.sizes.width); // 51, see the result from the other one
To fix that, we should use the cloning loop that examines each value of user[key] and, if it’s an object, then replicate its
structure as well. That is called a “deep cloning”.
There’s a standard algorithm for deep cloning that handles the case above and more complex cases, called the Structured cloning
algorithm. In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library lodash, the
method is called _.cloneDeep(obj).
Summary
Objects are associative arrays with several special features.
Additional operators:
What we’ve studied in this chapter is called a “plain object”, or just Object .
They have their special features that we’ll study later. Sometimes people say something like “Array type” or “Date type”, but
formally they are not types of their own, but belong to a single “object” data type. And they extend it in various ways.
Objects in JavaScript are very powerful. Here we’ve just scratched the surface of the topic that is really huge. We’ll be closely
working with objects and learning more about them in further parts of the tutorial.
✔ Tasks
Hello, object
importance: 5
solution
Write the function isEmpty(obj) which returns true if the object has no properties, false otherwise.
solution
Constant objects?
importance: 5
1 const user = {
2 name: "John"
3 };
4
5 // does it work?
6 user.name = "Pete";
solution
1 let salaries = {
2 John: 100,
3 Ann: 160,
4 Pete: 130
5 }
Write the code to sum all salaries and store in the variable sum . Should be 390 in the example above.
solution
For instance:
Please note that multiplyNumeric does not need to return anything. It should modify the object in-place.
solution
Comments
● You're welcome to post additions, questions to the articles and answers to them.
● To insert a few words of code, use the <code> tag, for several lines – use <pre> , for more than 10 lines – use a
sandbox (plnkr, JSBin, codepen…)
● If you can't understand something in the article – please elaborate.
contact us
RU / EN