Javascript: Can't print object property defined by Object.defineProperty - javascript

I use Object.defineProperty method to define a property of an object:
const o = { a: 1 }
Object.defineProperty(o, 'b', {
get() {
return this.a
}, set(value) {
this.a = value
}
})
However, when I am using console.log try to print object o, the b property seem not in it.
But when I try to use o.b to access to it, it can return correct value
So I am confused: why the property can't be printed but can be accessed?

Properties added with .defineProperty() are by default non-enumerable.
Make them "visible" by setting enumerable: true
var foo = {}
Object.defineProperty(foo, "a", {
get() { return "a"; }
});
Object.defineProperty(foo, "b", {
get() { return "b"; },
enumerable: true
});
console.log(Object.keys(foo));

Well, you have own enumerable properties and all own properties of an object.
To get all enumerable own properties, you could take Object.keys and for getting the non-enumerable properties as well, you coul take Object.getOwnPropertyNames.
Maybe console.log shows only the enumerable properties of an object. This implementation is up to the vendor.
const o = { a: 1 }
Object.defineProperty(o, 'b', {
get() {
return this.a
}, set(value) {
this.a = value
}
})
console.log(o);
console.log(Object.keys(o));
console.log(Object.getOwnPropertyNames(o));
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can get propertyNames using Object.getOwnPropertyNames
const o = { a: 1 }
Object.defineProperty(o, 'b', {
get() {
return this.a
}, set(value) {
this.a = value
}
})
console.log(Object.getOwnPropertyNames(o))

Related

How to use variable value directly in a dynamically created JavaScript getter?

I have the following code for adding getters to an object on the fly:
const obj = {};
const uni = { name1: ..., name2: ..., ...};
for(const nm in uni) {
const u = obj[nm] = {};
Object.defineProperty(u, 'value', {
configurable: true,
enumerable: true,
get: () => { return uni[nm] },
});
}
I want getters to return different values, so I want uni[nm] to be understood literary, i.e. the first getter would return uni.name1, the second uni.name2 etc.
How do I do that?
EDIT:
I want to dynamically create an object like this:
{
name1: { get value() { return uni.name1 }},
name2: { get value() { return uni.name2 }},
...
}
Don't use for..in. And you pretty much never need Object.defineProperty. That's all stone age JavaScript.
Instead use -
for..of to iterate over Object.keys.
get keyword to define a getter
const uni = { name1: "foo", name2: "bar" }
let obj = {}
for(const key of Object.keys(uni)) {
obj[key] = {
get value() {
return uni[key]
}
};
}
console.log(obj.name1.value) // "foo"
console.log(obj.name2.value) // "bar"
uni.name1 = "zzz"
console.log(obj.name1.value) // "zzz"
This can be expressed using array.reduce and -
object spread to extend the definition of an object
computed properties to define a dynamic key -
const uni = { name1: "foo", name2: "bar" }
const obj = Object.keys(uni).reduce(
(o, key) => {
return {...o, [key]: { get value() { return uni[key] }}} // extend
},
{} // initial object
)
console.log(obj.name1.value) // "foo"
console.log(obj.name2.value) // "bar"
uni.name1 = "zzz"
console.log(obj.name1.value) // "zzz"
You can achieve this by using the square bracket notation ([]) to dynamically access properties in the uni object, like this:

Can I use an object as a template for other objects?

I'm trying to reduce the amount of duplicate code i'm writing in JS objects. I have a methods that I want to use where almost nothing is changing except the target and I'd like to extract that out and somehow get the target through the objects property name. Hopefully the example I put together makes sense.
myObject = {
d: {
get: function(list, id) {
// do stuff
},
prop1: {
data: [],
list: myObject.config.lists.prop1.guid,
get: function(a,b) {
myObject.d.get(a,b)
}
},
// I want to write this once and use the object key ("prop2") as an argument
prop2: {
data: [],
list: myObject.config.lists.prop2.guid,
get: function(a,b) {
myObject.d.get(a,b)
}
}
}
};
Tried something like this but getting error "Cannot read prop 'spec' of undefined:"
myObject = {
d: {
get: function(list, id) {
// do stuff
}
},
// Use this to duplicate shared funtions for similar
spec: function(target) {
return {
data: [],
list: myObject.config.lists[target].guid,
get: function() {
myObject.d.get(a, b);
},
update: "",
delete: ""
};
},
// some how return `myObject.spec.get()`, allowing me to use myObject.d.prop1.get()
prop1: myObject.spec.apply(this, "prop1"),
prop2: myObject.spec.apply(this, "prop2")
};
So far the only way I was able to get it working was by setting prop1 and prop2 outside of the initial deceleration like this and explicitly declaring the target like #Bergi suggested:
var myObject = myObject || {};
myObject = {
d: {
get: function(list, id) {
// do stuff
}
},
// Use this to duplicate shared funtions for similar
spec: function(target) {
return {
data: [],
list: target,
get: function() {
myObject.d.get(a, b);
},
update: "",
delete: ""
};
}
};
// some how return `myObject.spec.get()`, allowing me to use myObject.d.prop1.get()
myObject.prop1 = myObject.spec("prop1");
myObject.prop2 = myObject.spec("prop2");

how to object value to its on property object?

how to object value to its on property object ?
I tried like this
var obj = {
a:2,
b:this.a
}
Then try obj.b it is giving undefined ..can I make function in object?
Expected output 2
const obj = {
a: 2,
get b() {
return this.a;
}
};
console.log(obj.b);
const obj2 = {
a: 2,
b: function() {
return this.a;
}
};
console.log(obj2.b());
See it in action: https://jsfiddle.net/rqnbxw86/

Javascript generic method to parse object of different parametrs

var data = {
'id': 'object1',
'sceneCapability': {
'updatedAt': '2017-06-19T20:52:45.688Z'
'currentScene': {
'value': {
'number': 1,
'name': '1'
}
},
'outOfTune': {
'value': false
}
},
'lightingCapability': {
'intensity': {
'value': 0
}
},
'tiltCapability': {
'command': {
'value': 'NO'
},
'position': {
'value': 0
}
}
// like this I have different types of more than 20 Capabilities
};
How can I write a generic method to parse this Object? I need to get currentScene value, outOfTune, intensity, command, position, etc...
Sometimes I get only one capability and sometime I get more than 20 capabilities.
I want to avoid doing something like this because in future there might be hundreds of different capabilities
if (obj.lightingCapability && obj.lightingCapability.intensity) {
console.log(obj.lightingCapability.intensity.value)
}
if (device.sceneCapability && device.sceneCapability.outOfTune) {
// do something
}
Output I want something like
currentScene:1,
outOfTune: false,
intensity: 0,
command: 'NO',
position: 0
Maybe something like this will work for you?
A helper function that finds the property you need and returns null if anything along the chain doesn't exist. I added two 'different' versions in case you don't like the array of property names.
var object = {
a: {
b: {
c: {
d: 10
}
}
}
};
function getValue(object, propertyPath) {
var o = object;
var pLen = propertyPath.length;
for (var i = 0; i < pLen; i++) {
var propertyName = propertyPath[i];
if (!o.hasOwnProperty(propertyName))
return undefined;
o = o[propertyName];
}
return o;
}
function getValueFromString(object, path) {
return this.getValue(object, path.split('.'));
}
console.log(getValue(object, ['a', 'b', 'c', 'd'])); //logs 10
console.log(getValueFromString(object, 'a.b.c.d')); //logs 10
console.log(getValue(object, ['a', 'b', 'c', 'e'])); //logs undefined
Based on the discussion we had in the comments of my first answer I realized you meant something different. This should do the trick:
var object = {
a: {
b: {
c: {
value: 10
},
d: {
e: {
value: 20
}
}
}
}
};
function logAllValues(object) {
for (var p in object) {
var o = object[p];
if (o.value)
console.log(p + ': ' + o.value);
else
logAllValues(o);
}
}
logAllValues(object); //logs c:10 and e:20
A slightly hacky way to do this would be to create a helper function that allows the key chain to be passed in as a string and loop over it. For example
function getValue(obj, keyChain){
var keys = keyChain.split('.');
do {
var key = keys.shift();
if (!obj.hasOwnProperty(key)){
return undefined;
}
obj = obj[key];
} while (keys.length > 0);
return obj;
}
getValue(data, "lightingCapability.intensity.value")
I think you just need to install lodash#get
npm i --save lodash.get
var get = require('lodash.get');
if(get('foo.baz.foobaz')) {
alert('yep');
}
but you always will need to know all the paths you need in advance.
Re-implementing this well community tested method will end up in re-inventing the wheel, so, just install and use it.
you can implement some thing like this using ES6 try and catch block
var object = {
a: {
b: {
c: {
value: 10
},
d: {
e: {
value: 20
}
}
}
}
};
function getValue(jsObject) {
try {
return jsObject();
} catch (e) {
return undefined;
}
}
// use it like this
getValue(() => object.a.b); // returns Object {c: Object, d: Object}
getValue(() => object.a.b.c); // returns Object {value: 10}
getValue(() => object.a.b.x); // returns undefined

What's the best way to delete object member and not have undefined left behind

What's the best way to delete object member and not have undefined left behind
as in
var o = {
a: 1,
b: 2,
c: 3
};
delete o['a']
for a for loop over o will get us an undefined member.
I already have lodash running but _.unset doesn't seem to do the trick
thanks,
Sean.
delete is the way. As adeneo said, delete doesn't leave anything behind. Once you delete o.a, o doesn't have the a property at all.1 The only reason you'd see undefined would be if you asked for the property anyway, because asking for the value of a non-existent property on an object gives you back undefined.
for a for loop over o will get us an undefined membe
Not any loop that works on the basis of o's actual properties. For instance, not this one:
var o = {
a: 1,
b: 2,
c: 3
}
delete o.a; // delete o['a']; also works
for (var name in o) {
console.log(name + ": " + o[name]);
}
1 Actually, it's possible to create a property that can't be deleted (by making the property non-configurable), but normal properties can be deleted. Here's how you'd create a non-configurable property:
var o = {};
Object.defineProperty(o, "a", {
value: 42,
writable: true,
enumerable: true
});
console.log(o.a); // 42
delete o.a;
console.log(o.a); // still 42
By not including configurable: true, we made the property non-configurable.
If you use strict mode, attempts to delete a non-configurable property are errors:
"use strict";
var o = {};
Object.defineProperty(o, "a", {
value: 42,
writable: true,
enumerable: true
});
console.log(o.a); // 42
delete o.a; // throws error
console.log(o.a);
When you attempt to get a property which does not exists, you get undefined. If you don't want that define a method that throws.
Object.defineProperty(o, 'a', {
get: () => { throw Error("You can't access this property"); }
});
var o = {
a: 1,
b: 2,
c: 3
}
Object.defineProperty(o, 'a', {
get: () => { throw Error("You can't access this property"); }
});
o.a; // Error: You can't access this property
You might also be interested in proxy objects:
var o = new Proxy(obj, {
get: function(target, property, receiver) {
if (property in target) return Reflect.get(target, property, receiver);
throw Error("The property does not exist");
}
});
var o = new Proxy({
a: 1,
b: 2,
c: 3
}, {
get: function(target, property, receiver) {
if (property in target) return Reflect.get(target, property, receiver);
throw Error("The property does not exist");
}
});
console.log(o.a); // 1
delete o.a;
console.log(o.a); // Error: The property does not exist

Categories