After doing some research on this problem, it turns out there is actually a method to the madness.
This one wasn't immediately obvious from my testing, but it is quite useful to understand wrt the examples that worked.
console.log(source[srcKeys]);
console.log(source[[[[srcKeys]]]]);
console.log(source[[],[[[srcKeys]]]]);
console.log(source[[],[[srcKeys]]]);
console.log(source[[[]],[],srcKeys]);
console.log(source[[[]],[[srcKeys]]]);
You will notice there is actually a pattern that was not initially obvious with these statements.
Each property accessor is either a comma expression (which will evaluate to the last value in the expression - usually an array) or just an array.
console.log(source[srcKeys]); // => array
console.log(source[[[[srcKeys]]]]); // => array
console.log(source[[],[[[srcKeys]]]]); // => comma expression
console.log(source[[],[[srcKeys]]]); // => comma expression
console.log(source[[[]],[],srcKeys]); // => comma expression
console.log(source[[[]],[[srcKeys]]]); // => comma expression
What's more, each array is either a single element array or a nested array of single element arrays.
Armed with this knowledge, it is now a good time to try to understand how the ECMAScript spec lays out the evaluation of property accessors
.
I'll spare you the gory details and instead give a summary of what occurs nearly 99% of the time:
The property expression is first converted to string (unless it is a Symbol since es6), then a reference to the value which has that given property name is returned
So now we understand that JavaScript will simply attempt to convert any property accessor to a string, and use that string as the property name.
In addition to that, arrays have an interesting behaviour when converted to string. You can read this other answer to have an indepth understanding on how an array is transformed to a property name.
The not soo secret reveal
Let's now apply everything we know to the examples given and see why some worked and some didn't.
First, let's look at the examples that didn't work:
console.log(source[[[],[[srcKeys]]]]); // => property accessor expression is [[],[[srcKeys]]]
console.log(source[[[],[],[[srcKeys]]]]); // => property accessor expression is [[],[],[[srcKeys]]]
console.log(source[[[[]],[[srcKeys]]]]); // => property accessor expression is [[[]],[[srcKeys]]]
One consistent detail of each of those expressions is that they are all arrays of multiple elements. It quickly becomes obvious why these didn't give us the expected value, but the previous ones did. For that, I will now show the result of toString
on all the arrays.
const source = {
last: "Capulet"
};
const srcKeys = Object.keys(source);
// console.log(source[srcKeys]);
console.log(srcKeys.toString());
// console.log(source[[[[srcKeys]]]]);
console.log([
[
[srcKeys]
]
].toString());
// console.log(source[[],[[[srcKeys]]]]);
console.log(([], [
[
[srcKeys]
]
]).toString());
// console.log(source[[],[[srcKeys]]]);
console.log(([], [
[srcKeys]
]).toString());
// console.log(source[[[]],[],srcKeys]);
console.log(([
[]
], [], srcKeys).toString());
// console.log(source[[[]],[[srcKeys]]]);
console.log(([
[]
], [
[srcKeys]
]).toString());
// console.log(source[[[],[[srcKeys]]]]);
console.log([
[],
[
[srcKeys]
]
].toString());
// console.log(source[[[],[],[[srcKeys]]]]);
console.log([
[],
[],
[
[srcKeys]
]
].toString());
// console.log(source[[[[]],[[srcKeys]]]]);
console.log([
[
[]
],
[
[srcKeys]
]
].toString());
And there we have it. First examples that worked, all have a toString value of last
, while the rest didn't.