DEV Community

Cover image for JS Objects, Prototypes, and Classes Simplified (Part 2)
Waseem Mansour
Waseem Mansour

Posted on

JS Objects, Prototypes, and Classes Simplified (Part 2)

Let's continue our journey with Part 2 of this series:

So far we used object literal and constructor functions to create objects. Actually, there's another way that was introduced in ECMA Script 5, Object.create() method.

Object.create()

This method accepts two parameters

  • 1st param is (mandatory): Object prototype (an object OR null).
  • 2nd param is (optional) : PropertiesObject (a set of property descriptors)

We won't focus on what a prototype is, for now, let's just say that it's something the object to be created, will be based on.

What's really interesting now is the property descriptors? What is that?

As the name implies, It describes or gives us information about the property at hand.

Let's clarify this with an example:

let person = Object.create(
  {}, // prototype
  {
    firstName: { // property name
      value: "Waseem", // property value
      writable: true, // property value can be overridden after creation or not
      enumerable: true, // is property accessible when the object’s properties are enumerated using the for...in loop or Object.keys() method.
      configurable: true, // does user has permission to change property descriptor, such as changing writable or enumerable.
    },
    lastName: {value: 'Mansour', writable: false, enumerable: false, configurable: true}
    // and another property and so on...
  }
);
Enter fullscreen mode Exit fullscreen mode
  • If configurable is set to false, this can't be undone, we won't have the ability to change this property descriptor anymore, which can be helpful when you don't want anyone to change the recommended behavior of your object.
// because lastName property writable option is set to false,
// this line won't have any effect on its value
person.lastName = 'Adam';
console.log(person.lastName); // => Mansour;

// Listing all object's properties names (keys)
let personKeys = Object.keys(person); // returns array of object keys
console.log(personKeys); // => ["firstName"] 
// Because "lastName" enumerable is set to false,
// It's not listed in object's array of keys
Enter fullscreen mode Exit fullscreen mode

Properties like firstName and lastName in the example above, are properties of a type called Data Properties.

The other type of object properties is Accessor Properties, which are functions executed when getting or setting a value.

Accessor descriptor for this type of properties differs from data properties descriptor.

They have get getter method and set setter method, instead of value and writable.

Let's add fullName property as Accessor property to the above example:

let person = Object.create(
  {}, // prototype
  {
    firstName: { // data property
      value: "Waseem",
      writable: true,
      enumerable: true,
      configurable: true
    },
    lastName: { // data property
      value: 'Mansour',
      writable: false,
      enumerable: false, 
      configurable: true
    },
    fullName: { // accessor property
      get() {
        return `${this.firstName} ${this.lastName}`;
      },
      set(value) {
        // Splitting full name to two strings using split method
        // & assign returned array of names to first and lastname
        // using destructuring.
        [this.firstName, this.lastName] = value.split(" ");
      },
      enumerable: false,
      configurable: true
    }
  }
);

// Setting fullName
// We use it as if it were a normal property, not a method
// Using assignment operator.
person.fullName = 'Magdy Mansour'; 

// Getting fullName
// The same, as if a normal property, not a method.
console.log(person.firstName); // => Magdy
Enter fullscreen mode Exit fullscreen mode

What if we've already an object created with object literal.
How to view the descriptor for a specific property?

Let's find how to accomplish this the following method:
Object.getOwnPropertyDescriptor(objName, propertName);

let car = {
  brandName: 'BMW',
  model: '320i',
}

let brandNameDescriptor = Object.getOwnPropertyDescriptor(car, 'brandName'));
console.log(brandNameDescriptor); // => 
// {
//   configurable: true
//   enumerable: true
//   value: "BMW"
//   writable: true
// }
Enter fullscreen mode Exit fullscreen mode

Object.defineProperty(obj, prop, descriptor)
This method will allow us to add a new property with descriptor or even modify an existing property descriptor.

Object.defineProperty(car, 'modelYear', {
  configurable: true,
  enumerable: true,
  value: 2020,
  writable: true
});
Enter fullscreen mode Exit fullscreen mode

Before we end talking about property descriptors, there are other Object related methods that affect property descriptors.

Object.freeze() and Object.seal() methods.

Object.freeze(objToBeFrozen)
This will prevent adding or removing properties from the frozen object, was it data properties or accessor properties.
Data properties value cannot be changed and writable and configurable is set to false
Accessor properties will act as if it's working, but it's not.

Note that property values of type Object can still be changed unless it's frozen too.

Object.seal() definition on MDN, clarify how it is different from Object.freeze().

The Object.seal() method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable.

With that said let's move on to the last section of this post.

How to list and access Objects properties?

Using for ... in loop:

let employee = {
  name: 'John',
  age: 32,
  isActive: true // assume this is not an enumerable property
};

// for in loop 
// will iterate over enumerable properties of this object.
// We can access both property key and value 

for (const key in employee) { // key variable name can be any name ;)
  // print object properties keys ( properties names ) to console.
  console.log(key); 

  // print object properties values to console.
  console.log(employee[key]); 
}
// Output: 
// => name
//    John
//    age
//    32
Enter fullscreen mode Exit fullscreen mode

Using Object.keys(), Object.values(), and Object.entries() methods:

// Object.keys() => 
// returns array of properties keys
let keys = Object.keys(employee);
console.log(keys); // => ['name', 'age'];

// Object.values() => 
// returns array of properties values

let values = Object.values(employee);
console.log(values); // => ['John', 32];

// Object.entries() =>
// returns array of properties in [key, value] pair
let properties = Object.entries(employee);
console.log(properties); // => [['name', 'john'], ['age', 32]];

// * Because isActive is not enumerable property
// It's not listed in any of the above methods
Enter fullscreen mode Exit fullscreen mode

And here we come to the end of this post.
Thank you for reading, please feel free to share with me your thoughts about this series so far.

See you in the next part of this series, keep practicing ;)

Top comments (0)