I have initialized an object that I wish to add to dynamically. Specifically I want to add array of objects to this object. I have tried the below but neither work.. is there a way to do this correctly? The final output should be object1.property1[0].value = 42 and object1.property1[0].writable = false.
const object1 = {};
Object.defineProperty(object1, 'property1', '' );
object1.property1 = [{value: 42, writable: false}];
const object1 = {};
Object.defineProperty(object1, 'property1', [{value: 42, writable: false}] );
Try using value property from descriptor argument:
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: [{
value: 42,
writable: false
}]
});
console.log(object1.property1[0].value);
console.log(object1.property1[0].writable);
The object descriptor must be declared as follow:
{value: [{value: 42}], writable: false}
const object1 = {};
Object.defineProperty(object1, 'property1', {value: [{value: 42}], writable: false});
console.log(object1.property1[0].value)
Since you specified you wanted to add an array of objects to the object, this solution is similar to other answers, but should demonstrate adding an array with more than one element:
const obj = {};
Object.defineProperty(obj, 'property', {
value: [{
value: 42,
writable: false
},
{
value: 55,
writable: false
}
]
});
console.log(obj.property[0].value);
console.log(obj.property[0].writable);
console.log(obj.property[1].value);
console.log(obj.property[1].writable);
Of course, your writable isn't going to do anything, since it'll be treated as just another property. Due to the fact that your property is an array of objects (e.g., [{...},{...}]), what you may want to do is iterate over that array and freeze those objects:
obj.property.forEach(o=>Object.freeze(o))
obj.property[0].value=33; // when frozen you won't be able to update the value
Related
Let's take 2 objects and let's say I want to dynamically add a key to the first object with the value of the second, and to do this I want to use the spread operator.
let object1 = { key: 'myKey1', value:1 };
let object2 = { field: 'key', displaValue:'chiave' };
something like this:
let object3 = {...object1, [object2.displayValue)]: object1[object2.field] }
But unfortunately I have this result with the undefined key:
{key: "myKey1", value: 1, undefined: "myKey1"}
Expected result:
{key: "myKey1", value: 1, chiave: "myKey1"}
In the console, I get abc despite setting {writable:false}. Could you explain how changing metadata works?
let portfolio = {
myFirstName: "Bob",
myLastName: "Alice",
myAge: 26,
aboutMe: function() {
return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
}
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
Object.defineProperty(portfolio, "myFirstName", { value: "abc" });
console.log(portfolio.myFirstName);
in your 2nd line Object.defineProperty(portfolio, "myFirstName", { value: "abc" }); you are defining the property again.
You are not assigning a value. You are tossing out the old property and replacing it with a brand spanking new one. (Technically incorrect, it goes through a lot of steps to evaluate and apply values to property properties, but for simple understanding, I believe this suffices as understanding, as it feels like a new one in this scenario. Please read the link if you wish to have the complex truth)
To assign a new value use portfolio.myFirstName = "value here" and you see it's write protected.
let portfolio = {
myFirstName: "Bob",
myLastName: "Alice",
myAge: 26,
aboutMe: function() {
return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
}
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
portfolio.myFirstName = "Alice";
console.log(portfolio.myFirstName);
to prevent the workaround, call Object.freeze() on the object after modifying its property. This will also have other side effects like not being able to edit the values of other properties.
let portfolio = {
myFirstName: "Bob",
myLastName: "Alice",
myAge: 26,
aboutMe: function() {
return ("First Name: " + this.myFirstName + "; Last Name: " + this.myLastName + "; Age: " + this.myAge + ";");
}
};
Object.defineProperty(portfolio, "myFirstName", { writable: false });
Object.freeze(portfolio);
Object.defineProperty(portfolio, "myFirstName", {value:"abc"});
console.log(portfolio.myFirstName);
writable: false only has an effect on Object.defineProperty if configurable is also set to false. See step 7 of the ValidateAndApplyPropertyDescriptor algorithm:
Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
If current.[[Configurable]] is false and current.[[Writable]] is false, then
If Desc.[[Writable]] is present and Desc.[[Writable]] is true, return false.
If Desc.[[Value]] is present and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false.
Return true.
That's likely because as long as a property is configurable, nothing stops you from changing the value of writable back to true, e.g.
Object.defineProperty(
portfolio,
"myFirstName",
{value: "abc", writable: true}
);
Note that any property declared as part of an object literal automatically has {writable: true, configurable: true, enumerable: true}.
Examples
Can't assign because writable and configurable are both false:
var obj = {};
Object.defineProperty(obj, 'test', {
value: 42,
configurable: false,
writable: false,
enumerable: true,
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 21});
console.log(obj);
Can assign a value because writable or configurable are true:
var obj = {};
Object.defineProperty(obj, 'test', {
value: 42,
configurable: true,
writable: false,
enumerable: true
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 21});
console.log(obj);
var obj = {};
Object.defineProperty(obj, 'test', {
value: 42,
configurable: false,
writable: true,
enumerable: true
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 21});
console.log(obj);
Lastly, if writable and configurable are both false but if the new value is the same as the current value, no error will be thrown since no change is actually being made to the property:
var obj = {};
Object.defineProperty(obj, 'test', {
value: 42,
configurable: false,
writable: false,
enumerable: true,
});
console.log(obj);
Object.defineProperty(obj, 'test', {value: 42});
console.log(obj);
Setting writable: false will work as expected for normal assignments (foo.bar = 42) because such assignments go through OrdinarySetWithOwnDescriptor which check the writable value of an existing property descriptor first.
You're working around that access restriction to hard-set the property. That only impacts the portfolio.myFirstName mutator.
You really can't block access to defineProperty like that. It's too low-level. That's probably a good thing, though, since it is dependable.
const a = {x:1};
console.log({...a}.x);
// 1
console.log(window.Math);
// Math {abs: ƒ, acos: ƒ, acosh: ƒ, asin: ƒ, asinh: ƒ, …}
console.log({...window}.Math);
// undefined
I don't understand why {...a}.x evaluates 1, but {...window}.Math evaluates undefined.
That's because Math is not enumerable.
The ECMA-2018 (ES9) specs is a little hard to read. MDN and a proposal page stated: {...obj} creates a new object for all of obj's (1) own and (2) enumerable properties. Math is window's own property, but not enumerable:
console.log(window.hasOwnProperty("Math"));
console.log(Object.getOwnPropertyDescriptor(window, "Math"));
You can reproduce the situation with an object:
obj = {};
Object.defineProperty(obj, 'x', {
enumerable: false,
value: 123
});
console.log(obj); // Google Chrome prints { x: 123 } but StackOverflow prints {} and Node.js prints {} too
console.log(obj.x);
console.log(({...obj}).x);
{...Window} is using the spread operator to make a copy into a new object. Math is not enumerable so it won't be copied to the new object.
You can test it by your self:
const a = {}
Object.defineProperty(a, "prop1", { value: "enumerable", enumerable: true })
Object.defineProperty(a, "prop2", { value: "not enumerable", enumerable: false })
Then copy your object:
{...a}.prop1 //"enumerable"
{...a}.prop2 // undefined
I have a JSON object(mainObj) which in turn has objects (say obj1, obj2, obj3). What I am trying to achieve is when I check for a condition iterating through every obj in the mainObj and if it holds true, I want to add only the name of that obj in an array of String. Something like,
for(obj in mainObj){
if(obj holds condition){
add the descriptor of the obj (in string format) to an array (not the entire obj)
}
You can use Object.keys() to iterate over your object keys, then use Array.filter() to filter the keys, here I am checking if the inner objects have a property show and if this property is truthy:
const mainObj = {
obj1: { show: true, a: 1 },
obj2: { show: false, a: 2 },
obj3: { a: 3 },
obj4: { show: true, b: 1 }
};
const result = Object.keys(mainObj).filter(key => mainObj[key].show);
console.log(result);
If you want to use a for-in loop, you have to make sure the property is part of the object and is not inherited from its protype chain using Object.hasOwnProperty():
const mainObj = {
obj1: { show: true, a: 1 },
obj2: { show: false, a: 2 },
obj3: { a: 3 },
obj4: { show: true, b: 1 }
};
const result = [];
for (const prop in mainObj) {
if (mainObj.hasOwnProperty(prop) && mainObj[prop].show) {
result.push(prop);
}
}
console.log(result);
If I have 2 type of objects:
object1 : {
value : { foo1: {}, foo2: 5 }, state: true, etc = {}
}
And
object2 : {
value : { foo1: { value: 5}, foo2: 6 }, state: true, etc = {}
}
If I do object1=object2 what exactly happens with object1 on all levels please.
I'm going to simplify that a bit:
var a = { value: 1, aStuff: true };
var b = { value: 2, bStuff: true };
b = a;
console.log(b); // { value: 1, aStuff: true }
Now a and b reference the same object. Think of it like the same object is accessible by two names. Which means this happens when you change that object:
a.value = 5
console.log(a); // { value: 5, aStuff: true }
Two names, one object.
So what happened to what to the { value: 2, bStuff: true } object? Once you tell b to reference a different object then no existing variable has a reference to it, so eventually the garbage collector will find it and dispose of it.
What happens with inner objects? That is the question..
Nothing at all. The outer object still holds references the values it contains. All that's changed is that you have two variables pointing to that same outer object.
object1 is now a reference of object2, any change in object1, will change object2;
var object1 = { foo: 'bar' };
var object2 = {
value : { foo1: { value: 5}, foo2: 6 }
};
object1 = object2; // the { foo: 'bar' } is gone.
object1.foo2 = 7; //This changes object2.foo2 value
console.log(object2.foo2); //7