Hello I found some difficulties when implementing javascript Object.defineProperties:
var book1 = {};
Object.defineProperties(book1, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get : function() {
return this._year;
},
set : function(newValue) {
if ((newValue - this._year) > 0) {
this.edition += 1;
} else if ((newValue - this._year) < 0) {
this.edition -= 1;
}
this._year = newValue;
}
}
});
book1.year = 2005;
document.write(book1.edition); //get 1, expect 2
document.write('<br/>');
book1.year = 2006;
document.write(book1.edition); //get 1, expect 3
document.write('<br/>');
Browser: Chrome 17.0.963.56
Any answer is welcome.
Thank you.
You have to specify writable: true as a property descriptor of _year. By default, it's not writable, and assigning a value to a non-writable property doesn't have any effect.
I strongly recommend to activate the strict mode, because you will receive an error message when the assignment of a value to a read-only property fails.
"use strict"; // <---
Object.defineProperties(book1, {
_year: {
value: 2004
writable: true /* <-- writable set to true*/
},
edition: {
value: 1,
writable: true
},
Related
obj = {}
obj.a = function(a) {
console.log(a); // returns undefined
//console.log('Obj.a')
}
obj.b = function(a) {
console.log(a); // returns undefined
//console.log('Obj.b')
}
var node = {
a: {
use:'a',
node:[
{
create:'item',
description:{name:'Samsung'},
}
]
},
b: {
use:'b',
node:[
{
create:'item',
description:{name:'Apple'},
}
]
}
}
for(name in node) {
if(node.hasOwnProperty(name)) {
a = node[name];
// console.log(name)
obj[name].call(a)
}
}
In the above code, why do I get undefined when calling functions obj.a and obj.b from the loop below, instead of the object that's being passed to it?
function.call. you should pass the object instance as first param
According to the MDN
Note: In certain cases, thisArg may not be the actual value seen by the method.
If the method is a function in non-strict mode, null and undefined will be replaced with the global object, and primitive values will be converted to objects.
obj = {}
obj.a = function(a) {
console.log(a); // returns undefined
//console.log('Obj.a')
}
obj.b = function(a) {
console.log(a); // returns undefined
//console.log('Obj.b')
}
var node = {
a: {
use: 'a',
node: [{
create: 'item',
description: {
name: 'Samsung'
},
}]
},
b: {
use: 'b',
node: [{
create: 'item',
description: {
name: 'Apple'
},
}]
}
}
for (name in node) {
if (node.hasOwnProperty(name)) {
a = node[name];
// console.log(name)
obj[name].call(obj,a)
}
}
I want make value attribute to read-only and i do these code but not work ??
Need help ?
const obj = {
name: "karl"
}
const origName = obj.name;
Object.defineProperty(obj, 'name', {
enumerable: false,
configurable: false,
get() {
return origName + 2;
}
});
You must add "writebale" key;
Like this;
Object.defineProperty(obj, "name", {
value: "karl",
writable: false
});
You could instead use the writable property of the property descriptor, which prevents the need for a get accessor:
Object.defineProperty(obj, 'name', {
value: "karl",
writable: false
get() {
return origName + 2;
}
});
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.
I have 2 objects. The first object A has a default value of 'enabled' for all properties.
public A(): any {
const enabledObject = { enabled: true };
return {
property1: enabledObject,
property2: enabledObject,
property3: enabledObject,
property4: enabledObject,
};
}
The second object has different properties
public B(): any {
const appProperty = {
property1: { enabled: true },
property2: { enabled: false},
property3: { enabled: true },
property4: { enabled: false},
};
}
I want to return object A minus any properties that are specified to be 'enabled=false' in object B.
public A(): any {
const enabledObject = { enabled: true };
return {
property1: enabledObject,
property3: enabledObject,
};
}
I am attempting to fix this with the following but it does not work.
return _.assignInWith(A, B, this.Disabled);
private Disabled(destination: any, source: any, key: any): any {
const disabledObject = { enabled: false };
if (_.isUndefined(destination)) {
return disabledObject;
}
return destination;
}
There is also a simple solution without using lodash:
function AmB(): any {
const a = A();
const b = B();
const res = {};
for (let aKey in a) {
if (!b[aKey] || b[aKey].enabled) {
res[aKey] = a[aKey];
}
}
return res;
}
Stackblitz demo of this example
While I do not know loadash syntax, this could be easily done through JavaSscript (or converted to Typescript)
var enabledObject = { enabled: true };
var A = {
property1: enabledObject,
property2: enabledObject,
property3: enabledObject,
property4: enabledObject
};
var B = {
property1: { enabled: true },
property2: { enabled: false},
property3: { enabled: true },
property4: { enabled: false}
};
var diff = {};
for(var p in A){
//diff[p] = {};
//if B does not have the property, add it to result and continue
if (!B.hasOwnProperty(p)) {
diff[p] = A[p];
} else {
if(B[p].hasOwnProperty('enabled') && !B[p]['enabled']) {
diff[p] = A[p];
}
}
}
just run this code on the console of your browser and print diff variable
If you wanted to, you could further generalize the logic to compare two object with different structure and return the differences
You can achieve the following using lodash as follows:
function AmB(): any {
const a = A();
const b = B();
const bPairs = _.toPairs(b);
const bNotEnabled = _.filter(bPairs, bPair => { return !bPair[1].enabled });
const aPairs = _.toPairs(a);
const aMinusB = _.reject(aPairs, aPair => _.find(bNotEnabled, b => b[0] === aPair[0]));
return {...aMinusB}
}
And you can see this working here
var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function() {
return this._year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
book.year = 2006;
alert(book.year); // 2004
alert(book.edition); // 1
Why do the alerts show the old property values, even though the setter should update the properties?
Make the properties writable as the default value of writable is false
var book = {};
Object.defineProperties(book, {
_year: {
value: 2004,
writable: true
},
edition: {
value: 1,
writable: true
},
year: {
get: function() {
return this._year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
book.year = 2006;
alert(book.year);
alert(book.edition);