Is there documentation in the Ecmascript standard that ensures that an array when passed as a property advisor has a defined format?

Asked

Viewed 73 times

3

The other day debugging a code here on the page I found something peculiar.
Like that:

let a = {};

let b = [1,2,3];
let c = [4,2,3];

a[b]= 1;
a[c]= 2;

console.log(a)       //{ "1,2,3": 1, "4,2,3": 2 }   <--- Preste atenção

In other words, the author of the question had passed a array as a property advisor using the bracket notation and could no longer locate the property.
This, however, showed me an interesting behavior of the language. An array, example ["1,2,3"], when passed as a property assessor using bracket notation with creates a property whose identifier is a string, in the example the string "1,2,3".

Using this behavior I created this code:

const regSlice = /^\d+(,\d+){0,2}$/;

function proxiedArray(array) {
  return new Proxy(array, {
    get: (obj, key) => {
      if (typeof key == "string") {
        if (regSlice.test(key)) {
          let [start, stop, step] = key.split(',').map((v) => parseInt(v));
          stop = (stop != undefined) ? stop : start + 1;
          step = (step != undefined) ? step : 1;
          let c = step - 1;
          return obj.slice(start, stop).filter((e, i) => ((c < step) ? c++ : c = 1, c % step == 0));
        }
      }
      return obj[key];
    }
  });
}

const a = proxiedArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

console.log(a[[0, 9, 2]]);            //[ 1, 3, 5, 7, 9]

Code that I tested in some Engins(Spidermonkey, V8) getting the same result achieved here.
The above code defines a function proxedArray(array) involving an object array in a Proxy giving the array a peculiar property, the power of being sliced by index such as a python list:

identificador_do_array"[" fatia "]"
fatia = array"[" inicio[, fim[, passo]] "]"

Where:

  • inicio is the index where the.
  • fim is the index where the slice will end. Optional.
  • passo is the variation between the indices of the elements to be considered successive. Optional. Missing is 1.

So before I further develop this code, and put it into production environment, I need to ask the following question::

  • There is documentation in the Ecmascript standard to ensure that an array when passed as a property advisor has a defined format?

I didn’t find anything about it, not saying yes or no.

2 answers

4


This is not unique to arrays. Any object, when used in a property provider, ends up having its method toString called, and this string is used as key. Ex:

var k = {
    toString: function() {
        console.log('chamando k.toString()');
        return 'k'; // esta será a chave
    }
}

var x = {};
x[k] = 'a';

console.log(x);
console.log(x.k);

The output of the code is:

chamando k.toString()
{ k: 'a' }
a

In the language specification we have the algorithm that evaluates the expression objeto[propriedade]. Basically, at some point the algorithm (which is complicated but "is just" follow the specification links) ends up calling the operation ToPropertyKey (here), that in the end calls toString.

In the case of arrays, the same occurs (the method toString is called and the result is used as key). Just change it to see:

var x = {};
var b = [1, 2, 3];
b.toString = function() {
    return this.join(' - ');
};
x[b] = 1;
console.log(x); // { '1 - 2 - 3': 1 }


This behaviour is also described in MDN documentation:

Property Names are string or Symbol. Any other value, including a number, is coerced to a string.

See also:

  • 1

    That, is that one the proof you were looking for and couldn’t find. Thank you!!!!

2

In Javascript, objects are collections of properties. However, an important constraint occurs for keys of these properties.

For design, the language chose to allow only keys of the type string or symbol. Thus, any other value used during the definition of a property will be converted to the type string.

The specification defines this semantic behavior here; the algorithm that makes the coercion in case the key is not string or symbol is defined in the abstract operation ToString (care not to confuse this operation with the method toString).

It is worth noting that the invocation of the method toString is just one consequence, and not a reason for such behaviour. If defined, the method qualified under the well known Symbol @@toPrimitive shall take priority over toString and valueOf.

See some examples:

const values = [
  '1', Symbol('2'), 3, 4n, [5, 6, 7], { 8: 8 }, function() { return 9; }, undefined, null, true, false
];

const obj = {};
for (const value of values) obj[value] = value;
console.log(obj);

Note that this behavior can be extremely undesirable in some cases. Out of curiosity, this is one of the reasons that led to the creation of Map, which has no such limitation.

  • 1

    Here ...this behavior may be extremely undesirable in some cases... i agree because in the case of instances of Object there is no possibility of use as key as they will always be converted into [object Object].

  • 1

    So, @Augustovasques, you could even try to "get around" by changing the toString or @@toPrimitive, but I think it would be gambiarra. In cases where this is the problem, use Map is really the best. :)

  • Yes it is the case to use a MAP. In my case I am implementing a Inversion List and one of the requirements is to extract by index a slice of the data.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.