Let's say I have a simple object a = {b: 2}
. I know of two ways to get the value of property b of a:
Dot notation: a.b // 2
and
Bracket Notation: a['b'] // 2
Practicality aside, does there exist any way to get the value of b from object a without using either of these two methods (dot notation and bracket notation)? MDN's page on Property Accessors only lists the 2 methods.
It's just a curiosity I had. I know there exist obscure ways to call functions without parenthesis, eg
parseInt`5.1`
I wanted to see if a similarly obscure thing was possible for Property access.
Let's say I have a simple object a = {b: 2}
. I know of two ways to get the value of property b of a:
Dot notation: a.b // 2
and
Bracket Notation: a['b'] // 2
Practicality aside, does there exist any way to get the value of b from object a without using either of these two methods (dot notation and bracket notation)? MDN's page on Property Accessors only lists the 2 methods.
It's just a curiosity I had. I know there exist obscure ways to call functions without parenthesis, eg
parseInt`5.1`
I wanted to see if a similarly obscure thing was possible for Property access.
Share edited Sep 21, 2018 at 2:02 Phil 165k25 gold badges262 silver badges267 bronze badges asked Sep 21, 2018 at 1:34 LucienLucien 1238 bronze badges 12- 1 Is there a reason for this question? – Phil Commented Sep 21, 2018 at 1:37
-
It depends upon what you are trying to do. For example, in your limited example
a[0] //2
as well. – Sablefoste Commented Sep 21, 2018 at 1:39 - @Sablefoste did you try executing that? – Phil Commented Sep 21, 2018 at 1:39
-
@phil, right, it gives
undefined
. But also the point is it could be used in a reference. For example: stackoverflow./a/24061635/1408137 – Sablefoste Commented Sep 21, 2018 at 1:49 - @Sablefoste for a plain object? I don't think so – Phil Commented Sep 21, 2018 at 1:50
4 Answers
Reset to default 5Don't forget Object.getOwnPropertyDescriptor()
:
const object1 = {
property1: 42
}
const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1');
console.log(descriptor1.value);//42
It doesn't search through the prototype chain, but it works on immediate properties and worth noting, for you can make recursive functions searching the prototype chain with it :)
This is not exactly the same as accessing a property, but it’s nevertheless a sneaky way to get at an object’s contents if you are using the latest JS (es6+). All the hip JS kids are doing it these days.
const { b } = a
console.log(b) // 2
This is called destructuring, it works with objects and arrays, and you can read more about it here: https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
First thing that springs to mind is Object.values
const a = {b: 2}
Object.values(a).forEach(v => {
console.info(v)
})
But how would you know what key you're accessing?
There's also Object.entries()
I suppose
const a = {b: 2}
Object.entries(a).forEach(entry => {
// entries are [key, value] arrays
let value = entry.pop()
let key = entry.pop()
console.info(key, ':', value)
})
Note: I used Array.prototype.pop()
so as not to use "bracket notation".
Here's an implementation of lodash 'get' and 'set' without using dot or bracket notation; useful for passing security scans.
https://jsfiddle/5amtL8zx/17/
/* lodash implementation of 'get', 'set', and 'unset' without dot or bracket notation
* - supports getting and setting 'prop1.2' array element but not with brackets: 'prop1.[2]'
*/
isObjectKey = (obj, key) => {
return Object.getPrototypeOf(obj) === Object.prototype && /string|number/.test(typeof key);
}
isArrayNumber = (obj, key) => {
const isKey = /string|number/.test(typeof key), path = isKey ? String(key).split('.') : [], prop = isKey && path.length > 1 ? path.shift() : '';
return Object.getPrototypeOf(obj) === Array.prototype && isKey && !isNaN(prop);
}
isValid = (obj, key) => {
const isObj = isObjectKey(obj, key), isArr = isArrayNumber(obj, key);
return isObj || isArr;
}
define = (obj, key, value) => {
Object.defineProperty(obj, String(key), { value, writable: true, configurable: true, enumerable: true });
}
get = (obj, key, value) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = String(key).split('.'), prop = path.shift(), result = new Map(Object.entries(obj)).get(prop);
return path.length && typeof result !== 'undefined' ? get(result, path.join('.'), value) : result || value;
}
set = (obj, key, value) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = key.split('.'), prop = path.shift();
if (!(prop in obj)) {
define(obj, prop, {});
}
const result = get(obj, prop);
return path.length && isValid(result, path.join('.')) ? set(result, path.join('.'), value) : define(obj, prop, value);
}
unset = (obj, key) => {
if (!isValid(obj, key)) {
return undefined;
}
let path = key.split('.'), prop = path.shift();
if (!(prop in obj)) {
return undefined;
}
if (path.length) {
let result = get(obj, prop);
result = unset(result, path.join('.'));
set(obj, prop, result);
return obj;
} else {
const { [prop]: remove, ...rest } = obj;
return rest;
}
}
let obj = {};
set(obj, 'prop1.prop2', 'value1');
console.log(Object.entries(obj));
console.log(get(obj, 'prop1.prop2'));
const prop1 = get(obj, 'prop1');
set(prop1, 'prop2', 'value2');
console.log(get(obj, 'prop1.prop2'));
set(obj, 'prop3', [1, 2, 3]);
console.log(get(obj, 'prop3'));
console.log(get(obj, 'prop3.2'));
console.log(get(obj.prop3, 0));
set(obj, 'prop3.3', 4);
console.log(get(obj, 'prop3.3'));
set(obj, 'prop4', [{'name': 'Bob'}]);
console.log(get(obj, 'prop4.0'));
unset(obj, 'prop4.0.name')
console.log(get(obj, 'prop4.0'));
//[["prop1", {
// prop2: "value1"
//}]]
//"value1"
//"value2"
//[1, 2, 3]
//3
//1
//4
//{
// name: "Bob"
//}
//{ ... }