In Javascript, we can add a property to an object with:
obj.property = value;
However, sometimes we would like to have fine-grained control over properties.
With Object.defineProperty(), we can decide:
| Descriptor | Purpose | Default |
|---|---|---|
| value | Property value | undefined |
| writable | Whether the property can be assigned | false |
| get | Getter of the property | undefined |
| set | Setter of the property | undefined |
| enumerable | Whether the property is visible to for-each loop | false |
| configurable | Whether the property can be redefined | false |
Object.defineProperty(obj, name, desc) expects three arguments:
- The
objargument is the object to which we would like to add a new property. - The
nameargument is the name of the new property. - The
descargument is the descriptor object of the new property. Ifenumerable,configurable, orwritableare not defined in the descriptor object, then the default valuefalsewill be assumed.
Value and Writable
The simplest usage is to define a property with a value. By default,
it will become a read-only property. Every assignment to the property will be
silently ignored.
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', { value: 32 });
// Get the value.
console.log(t.fahrenheit); // Prints 32.
// Set the value.
t.fahrenheit = 5; // Ignored silently.
console.log(t.fahrenheit); // Continue to print 32.
Under the strict mode, assigning to a non-writable property will raise a
TypeError instead:
'use strict'; // CHANGED
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', { value: 32 });
try {
// Set the value.
t.fahrenheit = 5; // Raise a TypeError exception.
} catch (ex) {
if (ex instanceof TypeError) {
console.log('CAUGHT EXPECTED EXCEPTION.');
} else {
console.trace(ex);
}
}
To define a property that can be assigned, we have to set writable to
true:
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', {
value: 32,
writable: true // CHANGED
});
t.fahrenheit = 41;
console.log(t.fahrenheit); // Prints 41
From some perspective, the code snippet above is similar to the following simple assignment:
var t = Object.create(null);
t.fahrenheit = 32; // Similar
t.fahrenheit = 41;
console.log(t.fahrenheit); // Prints 41
Note
The difference between two code snippets is the enumerability of the defined property. Read Enumerable section for more information.
Get and Set
Imagine a senario: we would like to create an object to represent a temperature in both celsius and fahrenheit. Besides, we would like to maintain the correspondence between them. What can we do? Getters and setters are the answers.
We can define a get or set function with
Object.defineProperty(). The get function can read values from
other properties or compute the result on-the-fly. Similarly, the set
function can write values to other properties. For example:
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', {
'get': function () {
return (this.celsius * 9 / 5 + 32);
},
'set': function (x) {
this.celsius = (x - 32) * 5 / 9;
}
});
t.celsius = 0;
console.log(t.fahrenheit + ' F'); // Prints: "32 F"
t.celsius = 50;
console.log(t.fahrenheit + ' F'); // Prints "122 F"
t.fahrenheit = 77;
console.log(t.fahrenheit + ' F'); // Prints "77 F"
console.log(t.celsius + ' C'); // Prints "25 C"
Similar to writable: false, if the set descriptor is not
specified, then the assignment operations to the property will be ignored
silently:
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', {
'get': function () {
return (this.celsius * 9 / 5 + 32);
}
});
t.celsius = 0;
t.fahrenheit = 77; // Silently ignored.
console.log(t.fahrenheit + ' F'); // Prints "32 F"
console.log(t.celsius + ' C'); // Prints "0 C"
Under the strict mode, a TypeError will be raised instead:
'use strict'; // CHANGED
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', {
'get': function () {
return (this.celsius * 9 / 5 + 32);
}
});
try {
t.fahrenheit = 77;
} catch (ex) {
if (ex instanceof TypeError) {
console.log('CAUGHT EXPECTED EXCEPTION.');
} else {
console.trace(ex);
}
}
Note
get and set can not be mixed with value and
writable.
Enumerable
By default, when Object.defineProperty() defines a new property, the
newly defined property won't be enumerable, i.e. the newly defined property
won't be enumerated in the for-each loop:
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', { value: 32 });
// Enumerate the keys.
for (var key in t) {
// This loop won't print anything because the `fahrenheit` property is
// NOT enumerable.
console.log('Found: ' + key);
}
If we wish to enumerate the newly defined property, then we can set
enumerable to true:
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', {
enumerable: true, // CHANGED
value: 32
});
// Enumerate the keys.
for (var key in t) {
console.log('Found: ' + key); // Prints: "Found: fahrenheit"
}
Configurable
By default, the property defined by Object.defineProperty() can not be
redefined or reconfigured. A TypeError will be raised if you try to do
so.
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', { value: 32 });
try {
// Redefine the property.
Object.defineProperty(t, 'fahrenheit', { value: 50 });
} catch (ex) {
if (ex instanceof TypeError) {
console.log('CAUGHT EXPECTED EXCEPTION.');
} else {
console.trace(ex);
}
}
If you really wish to redefine the property afterwards, then you should
set configurable to true.
var t = Object.create(null);
Object.defineProperty(t, 'fahrenheit', {
configurable: true, // CHANGED
value: 32
});
console.log(t.fahrenheit); // Prints: 32
// Redefine the property.
Object.defineProperty(t, 'fahrenheit', { value: 50 });
console.log(t.fahrenheit); // Prints: 50
Conclusion
In this post, we have learned several usages of Object.defineProperty():
- One can define a property with
valueand control its immutability withwritable. - One can define a property with getter and setter functions. The getter and
setter functions are specified by
getandsetdescriptors respectively. - To enumerate a property in a for-each loop, specify
enumerable. - To allow the reconfiguration of a property, specify
configurable.
I hope you enjoy this post. Feel free to let me know if you have any comments.
References
- MDN, JavaScript Reference, Object.defineProperty()





