Why do we use Object.defindProperty() in javascript? [duplicate] - javascript

I'm wondering when I should use
Object.defineProperty
to create new properties for an object. I'm aware that I'm able to set things like
enumerable: false
but when do you need this really? If you just set a property like
myObject.myprop = 5;
its descriptors are all set to true, right? I'm actually more curious when you guys use that rather verbose call to .defineProperty() and for what reasons.

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]"

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);

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:
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

Summary:
In Javascript Objects are collections of key-value pairs.
Object.defineProperty() is a function which can define a new property on an object and can set the following attributes of a property:
value <any>: The value associated with the key
writable <boolean>: if writable is set to true The property can be updated by assigning a new value to it. If set to false you can't change the value.
enumerable <boolean>: if enumerable is set to true Property can be accessed via a for..in loop. Furthermore are the only the enumerable property keys returned with Object.keys()
configurable <boolean>: If configurable is set to false you cannot change change the property attributes (value/writable/enumerable/configurable), also since you cannot change the value you cannot delete it using the delete operator.
Example:
let obj = {};
Object.defineProperty(obj, 'prop1', {
value: 1,
writable: false,
enumerable: false,
configurable: false
}); // create a new property (key=prop1, value=1)
Object.defineProperty(obj, 'prop2', {
value: 2,
writable: true,
enumerable: true,
configurable: true
}); // create a new property (key=prop2, value=2)
console.log(obj.prop1, obj.prop2); // both props exists
for(const props in obj) {
console.log(props);
// only logs prop2 because writable is true in prop2 and false in prop1
}
obj.prop1 = 100;
obj.prop2 = 100;
console.log(obj.prop1, obj.prop2);
// only prop2 is changed because prop2 is writable, prop1 is not
delete obj.prop1;
delete obj.prop2;
console.log(obj.prop1, obj.prop2);
// only prop2 is deleted because prop2 is configurable and prop1 is not

Object.defineProperty prevents you from accidentally assigning values to some key in its prototype chain. With this method you assign only to that particular object level(not to any key in prototype chain).
For example:
There is an object like {key1: value1, key2: value2} and you don't know exactly its prototype chain or by mistake you miss it and there is some property 'color' somewhere in prototype chain then-
using dot(.) assignment-
this operation will assign value to key 'color' in prototype chain(if key exist somewhere) and you will find the object with no change as .
obj.color= 'blue'; // obj remain same as {key1: value1, key2: value2}
using Object.defineProperty method-
Object.defineProperty(obj, 'color', {
value: 'blue'
});
// now obj looks like {key1: value1, key2: value2, color: 'blue'}. it adds property to the same level.Then you can iterate safely with method Object.hasOwnProperty().

One neat use case I have seen for defineProperty is for libraries to provide an error property to the user which, if it's not accessed within a certain interval you would log the error yourself. For example:
let logErrorTimeoutId = setTimeout(() => {
if (error) {
console.error('Unhandled (in <your library>)', error.stack || error);
}
}, 10);
Object.defineProperty(data, 'error', {
configurable: true,
enumerable: true,
get: () => {
clearTimeout(logErrorTimeoutId);
return error;
},
});
Source for this code: https://github.com/apollographql/react-apollo/blob/ddd3d8faabf135dca691d20ce8ab0bc24ccc414e/src/graphql.tsx#L510

A good use is when you need to do some interception or apply a classical Observer/Observable pattern in a elegant way:
https://www.monterail.com/blog/2016/how-to-build-a-reactive-engine-in-javascript-part-1-observable-objects

A very useful case is to monitor changes to something and act on them. It's easy because you can have callback functions fire whenever the value gets set. Here's a basic example.
You have an object Player that can be playing or not playing. You want something to happen right when it starts playing, and right when it stops playing.
function Player(){}
Object.defineProperty(Player.prototype, 'is_playing', {
get(){
return this.stored_is_playing; // note: this.is_playing would result in an endless loop
},
set(newVal){
this.stored_is_playing = newVal;
if (newVal === true) {
showPauseButton();
} else {
showPlayButton();
}
}
});
const cdplayer = new Player();
cdplayer.is_playing = true; // showPauseButton fires
This answer is related to a couple other answers here, which are good stepping points for more information, but with no need to follow external links to read about libraries or programming paradigms.

#Gerard Simpson
If 'area' should be enumerable it can be written without Object.defineProperty, too.
var myObj = {
get area() { return this.width * this.height }
};
myObj.width = 20;
myObj.height = 20;
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//area -> 400, width -> 20, height -> 20

Related

Error adding ownProperty objects to Object's prototype

I was adding some properties to objects using Object.defineProperty. It works normally when I add to the property of the object. But when I added it to the prototype of the object, I got a too much recursion error.
var obj = {};
Object.defineProperty(obj.__proto__, 'key', {
enumerable: true,
get: () => {return key},
set: (value) => {
if (typeof(value) == 'number'){
key = 2*value;
}
}
});
obj.key = 3;
console.log(obj.key);
Why can't objects in the prototype be added this way?
This is because by defining the property on the Object's prototype every object gets that getter / setter. And everything in JS is an Object so this means window is also going to have a key setter / getter.
Inside your setter / getter functions you just use key. Since you did not explicitly define a key variable it is going to use the one defined on window. This is what is causing the recursion.
You do obj.key = 3, which calls the setter which does, window.key = 2*value, which calls the setter, which does window.key = 2*value. So on and so forth till you reach maximum call stack.
Don't add something onto the prototype unless you are wanting it on every instance.
And since you are using arrow functions there is no this bound to the setter / getter calls and therefore you have no way of knowing which instance object you are interacting with.
Instead do not extend Object's prototype make one of your own, and set / get to the right variable / property.
For instance use actual constructor functions, and change arrow functions to regular function expressions. This will make it so the setter / getter can get a instance reference to the object. You will then need some way of keeping track of which key belongs to which instance. If not needing to hide these variables you could simply use prefixed named property, like: this._key = 2*value; Otherwise you can use WeakMaps if you want to keep them private.
function YourClass(){};
(function(){
var keyMap = new WeakMap();
Object.defineProperty(YourClass.prototype, 'key', {
enumerable: true,
get: function(){
return keyMap.get(this);
},
set: function(value){
if(typeof(value) == "number"){
keyMap.set(this,2*value);
}
}
});
})();
obj = new YourClass();
obj.key = 3;
console.log(obj.key);
function secondClass(){}
//make secondClass inherit from YourClass
secondClass.prototype = Object.create(
YourClass.prototype,
{
"constructor": {
configurable: true,
enumerable: false,
writable: true,
value: secondClass
}
});
var obj2 = new secondClass();
obj2.key = 16;
console.log(obj2.key);
Also since you seem ok with using ES6 you could just use Classes which have a set / get syntax
class YourClass {
constructor(){
this._key = null;
}
set key(value){
this._key = 2*value;
}
get key(){
return this._key;
}
}
class SecondClass extends YourClass {
constructor(){
super();
}
}
var obj = new SecondClass();
obj.key = 17;
console.log(obj.key);

Javascript object edited partially

I want to create a closed object in javascript, that can be edited only using Object.defineProperty and not to be edited it in the normal way...
The goal is, I am creating a lib, where users can read an object called dictionary, but they can edit it too! is there any way to have an object that can be read by users, and edited by me ?
It is just not possible to protect any object parts.
See also:
How to Create Protected Object Properties in JavaScript
You can provide some basic protection using Object.defineProperty like this:
var o = { a: 5 };
o._protected = {};
o._protected.a = o.a;
Object.defineProperty(o, 'a', {
get: function() { return this._protected.a; },
set: function(value) {
if (value > 0 && value < 5) {
this._protected.a = value;
}
configurable: false
});
This will constrain changes to property a in this object so they will go through the get (read) / set (update). Of course, in this case the _protected object can be manipulated but it does require the user to consciously 'hack' it. Attempts to directly change property a would be under your control.
In this example, an attempt to set o.a = 6 would result in no change to o.a (of course, you could set it to the maximum allowed value in your set function if that were preferable).
You can prevent changes to o.a by not providing a set function.
This can be handy for ensuring properties only get 'valid' values and I've often used it that way.
I found it! please tell me what's the wrong with this solution:
var protected = {}
Object.defineProperty(this,
'setter', {
value: function(name , value) {
protected[name] = value
},
writable: false,
})
Object.defineProperty(this,
'getter', {
value: function(name , value) {
return JSON.parse(JSON.stringify(protected))
},
writable: false,
})
Object.freeze(this.setter)
Object.freeze(this.getter)

how to use javascript Object.defineProperty

I looked around for how to use the Object.defineProperty method, but couldn't find anything decent.
Someone gave me this snippet of code:
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
})
But I don't understand it. Mainly, the get is what I can't get (pun intended). How does it work?
Since you asked a similar question, let's take it to step by step. It's a bit longer, but it may save you much more time than I have spent on writing this:
Property is an OOP feature designed for clean separation of client code. For example, in some e-shop you might have objects like this:
function Product(name,price) {
this.name = name;
this.price = price;
this.discount = 0;
}
var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10); // {name:"T-shirt",price:10,discount:0}
Then in your client code (the e-shop), you can add discounts to your products:
function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }
Later, the e-shop owner might realize that the discount can't be greater than say 80%. Now you need to find EVERY occurrence of the discount modification in the client code and add a line
if(obj.discount>80) obj.discount = 80;
Then the e-shop owner may further change his strategy, like "if the customer is reseller, the maximal discount can be 90%". And you need to do the change on multiple places again plus you need to remember to alter these lines anytime the strategy is changed. This is a bad design. That's why encapsulation is the basic principle of OOP. If the constructor was like this:
function Product(name,price) {
var _name=name, _price=price, _discount=0;
this.getName = function() { return _name; }
this.setName = function(value) { _name = value; }
this.getPrice = function() { return _price; }
this.setPrice = function(value) { _price = value; }
this.getDiscount = function() { return _discount; }
this.setDiscount = function(value) { _discount = value; }
}
Then you can just alter the getDiscount (accessor) and setDiscount (mutator) methods. The problem is that most of the members behave like common variables, just the discount needs special care here. But good design requires encapsulation of every data member to keep the code extensible. So you need to add lots of code that does nothing. This is also a bad design, a boilerplate antipattern. Sometimes you can't just refactor the fields to methods later (the eshop code may grow large or some third-party code may depend on the old version), so the boilerplate is lesser evil here. But still, it is evil. That's why properties were introduced into many languages. You could keep the original code, just transform the discount member into a property with get and set blocks:
function Product(name,price) {
this.name = name;
this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
var _discount; // private member
Object.defineProperty(this,"discount",{
get: function() { return _discount; },
set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
});
}
// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called
Note the last but one line: the responsibility for correct discount value was moved from the client code (e-shop definition) to the product definition. The product is responsible for keeping its data members consistent. Good design is (roughly said) if the code works the same way as our thoughts.
So much about properties. But javascript is different from pure Object-oriented languages like C# and codes the features differently:
In C#, transforming fields into properties is a breaking change, so public fields should be coded as Auto-Implemented Properties if your code might be used in the separately compiled client.
In Javascript, the standard properties (data member with getter and setter described above) are defined by accessor descriptor (in the link you have in your question). Exclusively, you can use data descriptor (so you can't use i.e. value and set on the same property):
accessor descriptor = get + set (see the example above)
get must be a function; its return value is used in reading the property; if not specified, the default is undefined, which behaves like a function that returns undefined
set must be a function; its parameter is filled with RHS in assigning a value to property; if not specified, the default is undefined, which behaves like an empty function
data descriptor = value + writable (see the example below)
value default undefined; if writable, configurable and enumerable (see below) are true, the property behaves like an ordinary data field
writable - default false; if not true, the property is read only; attempt to write is ignored without error*!
Both descriptors can have these members:
configurable - default false; if not true, the property can't be deleted; attempt to delete is ignored without error*!
enumerable - default false; if true, it will be iterated in for(var i in theObject); if false, it will not be iterated, but it is still accessible as public
* unless in strict mode - in that case JS stops execution with TypeError unless it is caught in try-catch block
To read these settings, use Object.getOwnPropertyDescriptor().
Learn by example:
var o = {};
Object.defineProperty(o,"test",{
value: "a",
configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings
for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable
If you don't wish to allow the client code such cheats, you can restrict the object by three levels of confinement:
Object.preventExtensions(yourObject) prevents new properties to be added to yourObject. Use Object.isExtensible(<yourObject>) to check if the method was used on the object. The prevention is shallow (read below).
Object.seal(yourObject) same as above and properties can not be removed (effectively sets configurable: false to all properties). Use Object.isSealed(<yourObject>) to detect this feature on the object. The seal is shallow (read below).
Object.freeze(yourObject) same as above and properties can not be changed (effectively sets writable: false to all properties with data descriptor). Setter's writable property is not affected (since it doesn't have one). The freeze is shallow: it means that if the property is Object, its properties ARE NOT frozen (if you wish to, you should perform something like "deep freeze", similar to deep copy - cloning). Use Object.isFrozen(<yourObject>) to detect it.
You don't need to bother with this if you write just a few lines fun. But if you want to code a game (as you mentioned in the linked question), you should care about good design. Try to google something about antipatterns and code smell. It will help you to avoid situations like "Oh, I need to completely rewrite my code again!", it can save you months of despair if you want to code a lot. Good luck.
get is a function that is called when you try to read the value player.health, like in:
console.log(player.health);
It's effectively not much different than:
player.getHealth = function(){
return 10 + this.level*15;
}
console.log(player.getHealth());
The opposite of get is set, which would be used when you assign to the value. Since there is no setter, it seems that assigning to the player's health is not intended:
player.health = 5; // Doesn't do anything, since there is no set function defined
A very simple example:
var player = {
level: 5
};
Object.defineProperty(player, "health", {
get: function() {
return 10 + (player.level * 15);
}
});
console.log(player.health); // 85
player.level++;
console.log(player.health); // 100
player.health = 5; // Does nothing
console.log(player.health); // 100
defineProperty is a method on Object which allow you to configure the properties to meet some criterias.
Here is a simple example with an employee object with two properties firstName & lastName and append the two properties by overriding the toString method on the object.
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
employee.toString=function () {
return this.firstName + " " + this.lastName;
};
console.log(employee.toString());
You will get Output as : Jameel Moideen
I am going to change the same code by using defineProperty on the object
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
value: function () {
return this.firstName + " " + this.lastName;
},
writable: true,
enumerable: true,
configurable: true
});
console.log(employee.toString());
The first parameter is the name of the object and then second parameter is name of the property we are adding , in our case it’s toString and then the last parameter is json object which have a value going to be a function and three parameters writable,enumerable and configurable.Right now I just declared everything as true.
If u run the example you will get Output as : Jameel Moideen
Let’s understand why we need the three properties such as writable,enumerable and configurable.
writable
One of the very annoying part of the javascript is , if you change the toString property to something else for example
if you run this again , everything gets breaks.
Let’s change writable to false. If run the same again you will get the correct output as ‘Jameel Moideen’ . This property will prevent overwrite this property later.
enumerable
if you print all the keys inside the object , you can see all the properties including toString.
console.log(Object.keys(employee));
if you set enumerable to false , you can hide toString property from everybody else. If run this again you will get firstName,lastName
configurable
if someone later redefined the object on later for example enumerable to true and run it. You can see toString property came again.
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
value: function () {
return this.firstName + " " + this.lastName;
},
writable: false,
enumerable: false,
configurable: true
});
//change enumerable to false
Object.defineProperty(employee, 'toString', {
enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));
you can restrict this behavior by set configurable to false.
Orginal reference of this information is from my personal Blog
Basically, defineProperty is a method that takes in 3 parameters - an object, a property, and a descriptor. What is happening in this particular call is the "health" property of the player object is getting assigned to 10 plus 15 times that player object's level.
yes no more function extending for setup setter & getter
this is my example Object.defineProperty(obj,name,func)
var obj = {};
['data', 'name'].forEach(function(name) {
Object.defineProperty(obj, name, {
get : function() {
return 'setter & getter';
}
});
});
console.log(obj.data);
console.log(obj.name);
Object.defineProperty() is a global function..Its not available inside the function which declares the object otherwise.You'll have to use it statically...
Summary:
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
});
Object.defineProperty is used in order to make a new property on the player object. Object.defineProperty is a function which is natively present in the JS runtime environemnt and takes the following arguments:
Object.defineProperty(obj, prop, descriptor)
The object on which we want to define a new property
The name of the new property we want to define
descriptor object
The descriptor object is the interesting part. In here we can define the following things:
configurable <boolean>: If true the property descriptor may be changed and the property may be deleted from the object. If configurable is false the descriptor properties which are passed in Object.defineProperty cannot be changed.
Writable <boolean>: If true the property may be overwritten using the assignment operator.
Enumerable <boolean>: If true the property can be iterated over in a for...in loop. Also when using the Object.keys function the key will be present. If the property is false they will not be iterated over using a for..in loop and not show up when using Object.keys.
get <function> : A function which is called whenever is the property is required. Instead of giving the direct value this function is called and the returned value is given as the value of the property
set <function> : A function which is called whenever is the property is assigned. Instead of setting the direct value this function is called and the returned value is used to set the value of the property.
Example:
const player = {
level: 10
};
Object.defineProperty(player, "health", {
configurable: true,
enumerable: false,
get: function() {
console.log('Inside the get function');
return 10 + (player.level * 15);
}
});
console.log(player.health);
// the get function is called and the return value is returned as a value
for (let prop in player) {
console.log(prop);
// only prop is logged here, health is not logged because is not an iterable property.
// This is because we set the enumerable to false when defining the property
}
import { CSSProperties } from 'react'
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'
export const COLOR_ACCENT = BLUE
export const COLOR_DEFAULT = BLACK
export const FAMILY = "'Segoe UI', sans-serif"
export const SIZE_LARGE = '26px'
export const SIZE_MEDIUM = '20px'
export const WEIGHT = 400
type Font = {
color: string,
size: string,
accent: Font,
default: Font,
light: Font,
neutral: Font,
xsmall: Font,
small: Font,
medium: Font,
large: Font,
xlarge: Font,
xxlarge: Font
} & (() => CSSProperties)
function font (this: Font): CSSProperties {
const css = {
color: this.color,
fontFamily: FAMILY,
fontSize: this.size,
fontWeight: WEIGHT
}
delete this.color
delete this.size
return css
}
const dp = (type: 'color' | 'size', name: string, value: string) => {
Object.defineProperty(font, name, { get () {
this[type] = value
return this
}})
}
dp('color', 'accent', COLOR_ACCENT)
dp('color', 'default', COLOR_DEFAULT)
dp('color', 'light', COLOR_LIGHT)
dp('color', 'neutral', COLOR_NEUTRAL)
dp('size', 'xsmall', SIZE_XSMALL)
dp('size', 'small', SIZE_SMALL)
dp('size', 'medium', SIZE_MEDIUM)
export default font as Font
Defines a new property directly on an object, or modifies an existing property on an object, and return the object.
Note: You call this method directly on the Object constructor rather
than on an instance of type Object.
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false, //If its false can't modify value using equal symbol
enumerable: false, // If its false can't able to get value in Object.keys and for in loop
configurable: false //if its false, can't able to modify value using defineproperty while writable in false
});
Simple explanation about define Property.
Example code: https://jsfiddle.net/manoj_antony32/pu5n61fs/
Object.defineProperty(Array.prototype, "last", {
get: function() {
if (this[this.length -1] == undefined) { return [] }
else { return this[this.length -1] }
}
});
console.log([1,2,3,4].last) //returns 4

Object property cannot be set via setter while using Object.create

On my attempt to learn more about Object.create I came across Object.create(): the New Way to Create Objects in JavaScript.
An example from the above page:
var Car2 = Object.create(null); //this is an empty object, like {}
Car2.prototype = {
getInfo: function() {
return 'A ' + this.color + ' ' + this.desc + '.';
}
};
var car2 = Object.create(Car2.prototype, {
//value properties
color: { writable: true, configurable:true, value: 'red' },
//concrete desc value
rawDesc: { writable: false, configurable:true, value: 'Porsche boxter' },
// data properties (assigned using getters and setters)
desc: {
configurable:true,
get: function () { return this.rawDesc.toUpperCase(); },
set: function (value) { this.rawDesc = value.toLowerCase(); }
}
});
car2.color = 'blue';
alert(car2.getInfo()); //displays 'A blue PORSCHE BOXTER.'
Question:
How correct is the above example? This answer seems to contradict the example above.It seems to give the notion that rawDesc could be a private member that could be modified only via getter/setter of desc. Is this useful in any way?
Also, trying to set value for desc using car2.desc = 'Merc' doesn't seem to work. Why is that so?
What parts of Object.defineProperty and Object.create are similar?
Research:
Somewhat related question: Why can I set [enumerability and] writability of unconfigurable property descriptors?
I have tried removing writable: false and value: 'Porsche boxter' and tried setting the value but to no avail.
Some comments:
var Car2 = Object.create(null); //this is an empty object, like {}
The comment isn't quite correct. The internal prototype of Car2 (i.e. its [[Prototype]]) will be null, so it doesn't inherit any properties of Object.prototype, whereas an object created using {} does.
Car2.prototype = {
...
};
var car2 = Object.create(Car2.prototype, {
It seems pointless to create Car2 since it isn't a function and can't be used as a constructor nor has it inherited any of the standard Object methods. It's just a placeholder for the object assigned to Car2.prototype. But I guess this is just an example.
And for the questions…
How correct is the above example? This answer seems to contradict the example above.
How does it contradict the example?
Also, trying to set value for desc using car2.desc = 'Merc' doesn't seem to work. Why is that so?
Because the setter for desc actually changes rawDesc, but rawDesc is set to writeable: false. Change it to writeable: true and the value is changed. But it's a public property anyway so a bit pointless to have its value set by setting some other property.
What parts of Object.defineProperty and Object.create are similar?
Object.create and Object.defineProperty are adjacent in ECMA–262, not hard to work it out. Basically, the first just adds a property to an existing object, the second creates a new object and sets its [[Prototype]] (i.e. it's equivalent to a constructor).

when do you use Object.defineProperty()

I'm wondering when I should use
Object.defineProperty
to create new properties for an object. I'm aware that I'm able to set things like
enumerable: false
but when do you need this really? If you just set a property like
myObject.myprop = 5;
its descriptors are all set to true, right? I'm actually more curious when you guys use that rather verbose call to .defineProperty() and for what reasons.
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]"
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);
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:
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
Summary:
In Javascript Objects are collections of key-value pairs.
Object.defineProperty() is a function which can define a new property on an object and can set the following attributes of a property:
value <any>: The value associated with the key
writable <boolean>: if writable is set to true The property can be updated by assigning a new value to it. If set to false you can't change the value.
enumerable <boolean>: if enumerable is set to true Property can be accessed via a for..in loop. Furthermore are the only the enumerable property keys returned with Object.keys()
configurable <boolean>: If configurable is set to false you cannot change change the property attributes (value/writable/enumerable/configurable), also since you cannot change the value you cannot delete it using the delete operator.
Example:
let obj = {};
Object.defineProperty(obj, 'prop1', {
value: 1,
writable: false,
enumerable: false,
configurable: false
}); // create a new property (key=prop1, value=1)
Object.defineProperty(obj, 'prop2', {
value: 2,
writable: true,
enumerable: true,
configurable: true
}); // create a new property (key=prop2, value=2)
console.log(obj.prop1, obj.prop2); // both props exists
for(const props in obj) {
console.log(props);
// only logs prop2 because writable is true in prop2 and false in prop1
}
obj.prop1 = 100;
obj.prop2 = 100;
console.log(obj.prop1, obj.prop2);
// only prop2 is changed because prop2 is writable, prop1 is not
delete obj.prop1;
delete obj.prop2;
console.log(obj.prop1, obj.prop2);
// only prop2 is deleted because prop2 is configurable and prop1 is not
Object.defineProperty prevents you from accidentally assigning values to some key in its prototype chain. With this method you assign only to that particular object level(not to any key in prototype chain).
For example:
There is an object like {key1: value1, key2: value2} and you don't know exactly its prototype chain or by mistake you miss it and there is some property 'color' somewhere in prototype chain then-
using dot(.) assignment-
this operation will assign value to key 'color' in prototype chain(if key exist somewhere) and you will find the object with no change as .
obj.color= 'blue'; // obj remain same as {key1: value1, key2: value2}
using Object.defineProperty method-
Object.defineProperty(obj, 'color', {
value: 'blue'
});
// now obj looks like {key1: value1, key2: value2, color: 'blue'}. it adds property to the same level.Then you can iterate safely with method Object.hasOwnProperty().
One neat use case I have seen for defineProperty is for libraries to provide an error property to the user which, if it's not accessed within a certain interval you would log the error yourself. For example:
let logErrorTimeoutId = setTimeout(() => {
if (error) {
console.error('Unhandled (in <your library>)', error.stack || error);
}
}, 10);
Object.defineProperty(data, 'error', {
configurable: true,
enumerable: true,
get: () => {
clearTimeout(logErrorTimeoutId);
return error;
},
});
Source for this code: https://github.com/apollographql/react-apollo/blob/ddd3d8faabf135dca691d20ce8ab0bc24ccc414e/src/graphql.tsx#L510
A good use is when you need to do some interception or apply a classical Observer/Observable pattern in a elegant way:
https://www.monterail.com/blog/2016/how-to-build-a-reactive-engine-in-javascript-part-1-observable-objects
A very useful case is to monitor changes to something and act on them. It's easy because you can have callback functions fire whenever the value gets set. Here's a basic example.
You have an object Player that can be playing or not playing. You want something to happen right when it starts playing, and right when it stops playing.
function Player(){}
Object.defineProperty(Player.prototype, 'is_playing', {
get(){
return this.stored_is_playing; // note: this.is_playing would result in an endless loop
},
set(newVal){
this.stored_is_playing = newVal;
if (newVal === true) {
showPauseButton();
} else {
showPlayButton();
}
}
});
const cdplayer = new Player();
cdplayer.is_playing = true; // showPauseButton fires
This answer is related to a couple other answers here, which are good stepping points for more information, but with no need to follow external links to read about libraries or programming paradigms.
#Gerard Simpson
If 'area' should be enumerable it can be written without Object.defineProperty, too.
var myObj = {
get area() { return this.width * this.height }
};
myObj.width = 20;
myObj.height = 20;
for (var key in myObj) {
if (myObj.hasOwnProperty(key)) {
console.log(key + " -> " + myObj[key]);
}
}
//area -> 400, width -> 20, height -> 20

Categories

Resources