Objects are one of the least understood features in JavaScript, since their implementation has some important differences from many more traditional programming languages.
Let’s go step by step and try to understand them.
In simple terms:
Objects are a collection of properties
To build objects, we can do it in two ways:
- Declarative or literal objects: We can create objects without needing a constructor or instantiating a class. We just declare the object and its properties.
const camilo = {
nombre: 'Camilo',
edad: 22,
sexo: 'masculino',
pasatiempos: ['patinar', 'bailar'],
hablar: function(){
return `hola soy ${this.nombre}, y tengo ${this.edad} años`;
}
}
console.log(camilo);
- Constructed objects: JavaScript is a class-free language, but we have the new keyword, which allows us to create a new object. This way we can use a function that plays the role of constructor.
function Persona(nombre, edad, sexo, pasatiempos) {
this.nombre = nombre;
this.edad = edad;
this.sexo = sexo;
this.pasatiempos = pasatiempos;
this.hablar = function() {
return `hola soy ${this.nombre}, y tengo ${this.edad} años`;
};
}
const camilo = new Persona('camilo', 22, 'masculino', ['patinar', 'bailar']);
console.log(camilo)
Content
JavaScript doesn’t store property content inside objects — it only stores property names with references to where the values are stored.
//pruebalo: https://jsbin.com/vukava/edit?js,console
const myObj = {
nombre: 'yeison',
hablar: function(){
return `hola soy ${this.nombre}`;
}
}
const myFunc = myObj.hablar;
myObj.hablar = null;
console.log(myObj.hablar); //null
console.log(myFunc); // function hablar() {..}
Accessing Properties
To access properties, we have two options:
- Dot notation with
. - Bracket notation with
[]
//Pruebalo https://jsbin.com/vusoqu/edit?js,console
const myObj = {
nombre: 'yeison',
hablar: function(){
return `hola soy ${this.nombre}`;
}
}
console.log(myObj.nombre); //yeison
const propiedad = 'nombre'
console.log(myObj[propiedad]); //yeison
Property Attributes
Each property has 4 attributes:
- value
- configurable
- enumerable
- writable
To view the attributes, we use Object.getOwnPropertyDescriptor(target, property)
// Pruebalo https://jsbin.com/dataku/edit?js,console
const myObj = {
nombre: 'yeison',
hablar: function(){
return `hola soy ${this.nombre}`;
}
}
var atributos = Object.getOwnPropertyDescriptor(myObj, 'nombre');
console.log(atributos);
/*
* [object Object] {
* configurable: true,
* enumerable: true,
* value: "yeison",
* writable: true
* }
*/
Knowing this, we can see our objects represented as:
const myObj = {
nombre: {
configurable: true,
enumerable: true,
value: "yeison",
writable: true
},
hablar: {
configurable: true,
enumerable: true,
value: function (){
return `hola soy ${this.nombre}`;
},
writable: true
}
}
Setting Attributes
To set new properties with custom attributes, we use Object.defineProperty(myObj, property, {attributes})
var myObject = {};
Object.defineProperty( myObject, 'a', {
value: 2,
writable: true,
configurable: true,
enumerable: true
} );
console.log(myObject.a); // 2
Let’s look at each of these attributes and better understand what they refer to.
Writable
This lets us define whether a property’s value can be modified or not.
const myObj = {}
Object.defineProperty(myObj,'a', {
value: 2,
writable: false, // no writable
configurable: true,
enumerable: true
})
myObj.a = 3;
console.log(myObj.a) // 2
Configurable
This lets us define whether a property’s attributes can be modified.
//pruebalo https://jsbin.com/danawo/edit?js,console
const myObj = {};
Object.defineProperty(myObj,'a', {
value: 2,
writable: true,
configurable: false, // no configurable
enumerable: true
});
Object.defineProperty(myObj,'a', {
value: 2,
writable: false, // no writable
configurable: true,
enumerable: true
}); //TypeError: Cannot redefine property: a
Enumerable
This controls whether the property will show up when the object’s properties are enumerated, such as when using for..in
var myObj = {
a: 1,
b: 2,
c: 3
};
Object.defineProperty(myObj, 'd', {
value: 4,
writable: true,
configurable: true,
enumerable: false
});
console.log(myObj); // {a: 1,b: 2,c: 3}
for(var item in myObj) {
console.log(item);
}
Useful Methods
- Object.preventExtensions(object) receives an object and returns one to which no new properties can be added.
var myObj = {
a: 1,
b: 2,
c: 3
};
var otherObj = Object.preventExtensions(myObj);
otherObj.d = 4; // Error
- Object.seal(object) receives an object and returns one to which no properties can be added, nor can existing ones be configured.
var myObj = {
a: 1,
b: 2,
c: 3
};
var otherObj = Object.seal(myObj);
var atributos = Object.getOwnPropertyDescriptor(otherObj, 'a');
console.log(atributos); //configurable: flase
otherObj.d = 4; // error
- Object.freeze(object) receives an object and returns one to which properties can’t be added, modified, or overwritten.
var myObj = {
a: 1,
b: 2,
c: 3
};
var otherObj = Object.freeze(myObj);
var atributos = Object.getOwnPropertyDescriptor(otherObj, 'a');
console.log(atributos); //configurable: flase, writable: false
otherObj.a = 4; // error
otherObj.d = 4; // error
Objects in JavaScript are dynamic entities that can be modified at any point. While this is a powerful feature, it’s not always what we want.
By applying the methods above, we can achieve immutable objects. However, if we have other objects as properties, the immutability doesn’t apply to them. We could create a recursive function that makes all of our objects’ properties immutable, or use libraries like immutable.js.