This is a part of my series of notes as I revisit core JS concepts.
Concepts covered:
- General object literals overview
- Dot notation vs Bracket notation
- Object References & Cloning
Constructor functions are not covered in this article.
In my last article, I had gone into the details of primitive data types in JS. This time it is the other data type in JS - objects.
As opposed to the primitive types, objects are used to store more complex entities - generally, a collection of related properties or methods.
A property is a “key: value” pair, where key is a string, and value can be anything (including array). A method is when the value is a function.
const user = {
name: 'Poulami',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
greet: function() {
alert(`Hi! I'm ${this.name}.`);
}
}
Some things to keep in mind:
- An object declared as
const
can be modified. It isn't possible to reassign 'user'
const user = {
name: 'Poulami',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
greet: function() {
console.log(`Hi! I'm ${this.name}.`);
}
}
user.greet(); // Hi! I'm Poulami.
user.name = "Unknown"
user.greet(); // Hi! I'm Unknown.
user = {
name: 'Unknown',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
greet: function() {
console.log(`Hi! I'm ${this.name}.`);
}
}
// Uncaught TypeError
There is no limitation to what key variable name can be (including 'let', 'return', etc.). Except for a variable named
_proto_
that has to be of type 'object'. (This is related to prototype, not included in this article)There will be no error if we try to access a property that doesn’t exist - it will return 'undefined'.
To check if the property exists , we can use thein
operator- it returns true if the specified property is in the object
user.age // undefined
'age' in user //false
Dot notation vs Bracket notation
There are two ways to access object properties - using dot notation, and using bracket notation.
//Get
user.name
user['name']
//Set
user.name = "Unknown"
user['name']= "Unknown"
Both essentially work in the same way, except in some cases where bracket notation is the only one which works.
- Using variables for key name
let requested = "handle";
console.log(user.requested) //undefined
console.log(user[requested]) //poulamic
- Multiword property names
const user = {
name: 'Poulami',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
"has published" : true, //multiword keys are in quotes
greet: function() {
console.log(`Hi! I'm ${this.name}.`);
}
}
user["has published"] // true
Within the object literal, square bracket is used for computed properties - i.e. while creating the literal, we set a property name through a variable;
let social = "twitter";
const user = {
name: 'Poulami',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
"has published" : true,
[`${social}account`]: true,
greet: function() {
console.log(`Hi! I'm ${this.name}.`);
}
}
console.log(user.twitteraccount) // true
Object References & Cloning
Primitives and objects behave differently when a variable is assigned a value through (equating with) a variable name.
let a = 5;
let b = a;
The expectation is that we have two independent variables 'a' & 'b' - both with value of 5. And that's true - when the value is of a primitive data type. (Under the hood, they are two different spots in memory).
let p = "poulami";
let q = p;
console.log(p,q) // poulami poulami
q= "unknown"
console.log(p,q) // poulami unknown
But it isn't so in case of objects. In case of objects, the actual object is stored somewhere in memory and the variable has a “reference” to it.
let user = {
name: 'Poulami',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
greet: function() {
alert(`Hi! I'm ${this.name}.`);
}
}
let user2 = user;
In this case, there are two variables, but only one value. The value has a point in memory & both the variables reference the same object. We can access the object and modify it through either variable, and changes are reflected when we try to access it through the other variable.
let user = {
name: 'Poulami',
handle: 'poulamic',
interests: ['UX', 'front-end', 'generative art'],
greet: function() {
alert(`Hi! I'm ${this.name}.`);
}
}
let user2 = user;
user2.name = "unknown";
console.log(user.name,user2.name) //unknown unknown
We can think of it like a shared document - in case of primitives, a variable is given a duplicated copy of the sheet which they can change without affecting the original; while in objects, the var is given author permission to the same sheet.
To have an independent copy, we need to clone the array. One way is to use the [Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
method.
let user2 =Object.assign({}, user);
user2.name = "unknown";
console.log(user.name,user2.name) //Poulami unknown
Seems to work! Let's try another one
let user2 = Object.assign({}, user);
user2.name = "unknown";
user2.interests[0] = 'Design'
console.log(user2.interests) // [ "Design", "front-end", "generative art" ]
console.log(user.interests) // [ "Design", "front-end", "generative art" ]
What happened?!
Well, the above method works with primitive values, but not object data types. Even within objects, nested objects (like arrays) behave as discussed above - variables are referencing to the object. So. when the (parent) object is cloned, it's the reference which is being copied.
Shallow and deep copy
A shallow copy is one in which only one level is copied while others are still connected to the original variable (through same reference). This will mostly work fine for an object containing only primitive values.
In a deep copy, on the other hand, all the values (including nested) have their own spot in memory and disconnected from the original variable.
This medium article explains several methods to shallow/deep copy objects and arrays in JavaScript.
References:
javascript.info
MDN
Top comments (0)