when do you use Object.defineProperty()

Object.defineProperty is mainly used to set properties with specific property descriptors (e.g. read-only (constants), enumerability (to not show a property in a for (.. in ..) loop, getters, setters).

"use strict";
var myObj = {}; // Create object
// Set property (+descriptor)
Object.defineProperty(myObj, 'myprop', {
    value: 5,
    writable: false
});
console.log(myObj.myprop);// 5
myObj.myprop = 1;         // In strict mode: TypeError: myObj.myprop is read-only

Example

This method extends the Object prototype with a property. Only the getter is defined, and the enumerability is set to false.

Object.defineProperty(Object.prototype, '__CLASS__', {
    get: function() {
        return Object.prototype.toString.call(this);
    },
    enumerable: false // = Default
});
Object.keys({});           // []
console.log([].__CLASS__); // "[object Array]"

A really good reason for using Object.defineProperty is that it lets you loop through a function in an object as a computed property, which executes the function instead of returning the function's body.

For example:

var myObj = {};

myObj.width = 20;
myObj.height = 20;

Object.defineProperty(myObj, 'area', {
    get: function() {
        return this.width*this.height;
    },
    enumerable: true
});

for (var key in myObj) {
  if (myObj.hasOwnProperty(key)) {
    console.log(key + " -> " + myObj[key]);
  }
}
//width -> 20, height -> 20, area -> 400

Versus adding the function as a property to an object literal:

var myObj = {};

myObj.width = 20;
myObj.height = 20;

myObj.area = function() {
       return this.width*this.height;
    };

for (var key in myObj) {
  if (myObj.hasOwnProperty(key)) {
    console.log(key + " -> " + myObj[key]);
  }
}
// width -> 20, height -> 20, area -> function() { return this.width*this.height;}

Make sure you set the enumerable property to true in order to loop through it.


For example, that's how Vue.js keeps track of changes in the data object:

When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty. This is an ES5-only and un-shimmable feature, which is why Vue doesn’t support IE8 and below.

The getter/setters are invisible to the user, but under the hood they enable Vue to perform dependency-tracking and change-notification when properties are accessed or modified.

[...]

Keep in mind that even a super slim and basic version of Vue.js would use something more than just Object.defineProperty, but the main functionality comes from it:

Vue.js's Reactivity Cycle

Here you can see an article where the author implements a minimal PoC version of something like Vue.js: https://medium.com/js-dojo/understand-vue-reactivity-implementation-step-by-step-599c3d51cd6c

And here a talk (in Spanish) where the speaker builds something similar while explaining reactivity in Vue.js: https://www.youtube.com/watch?v=axXwWU-L7RM


Features like 'enumerable' are rarely used in my experience. The major use case is computed properties:

var myObj = {};

myObj.width = 20;
myObj.height = 20;

Object.defineProperty(myObj, 'area', {
    get: function() {
        return this.width*this.height;
    }
});
console.log(myObj.area);