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 togetter/setters
usingObject.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:
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);