¿Por qué JavaScript convierte una matriz de una cadena en una cadena, cuando se usa como una clave de objeto?




(3)

Me encontré con un escenario en el que JavaScript se comporta de una manera que me resulta desconcertante.

Digamos que tenemos un objeto con dos teclas foo & bar.

a = { foo: 1, bar: 2 }

Entonces, tengo una serie de cadenas, en este caso un 'foo'

b = ['foo']

Yo esperaría lo siguiente:

a[b] == undefined
a[b[0]] == 1

PERO, esto es lo que sucede:

a[b] == 1
a[b[0]] == 1

¿Por qué JavaScript convierte ['foo'] -> 'foo' cuando se usa como clave?

¿Alguien por ahí sabe la razón?

¿Cómo se puede prevenir esto?

let a = { foo: 1, bar: 2 }
let b = ['foo']

console.log(a[b] == 1)    // expected a[b] to be undefined
console.log(a[b[0]] == 1)  // expected a[b] to be 1


Al usar una matriz como clave, javascript llama al método 'toString ()' de esa matriz y luego trata de encontrar la versión en cadena de la matriz como la clave. Y si llama ['foo'].toString() verá que este método devuelve "foo" .


Cuando se usa una matriz como clave, javascript llama al método 'toString ()' de esa matriz y luego intenta encontrar la versión en cadena de la matriz como la clave. Y si llama ['foo']. ToString () verá que este método devuelve "foo".


¿Por qué JavaScript convierte ['foo'] -> 'foo' cuando se usa como clave?
¿Alguien por ahí sabe la razón?

Cada vez que hay confusión sobre por qué JavaScript actúa de una manera que puede ser inesperada, entonces mirar la definición del lenguaje es la manera segura de averiguar exactamente qué sucedió.

https://www.ecma-international.org/ecma-262/10.0/ es la definición de idioma más actual en el momento de publicar esto.

Primero, querrá encontrar el área correspondiente al acceso a la matriz. Sin embargo, está en la jerga lingüística.

12.3.2.1 Semántica de tiempo de ejecución: evaluación
MemberExpression: MemberExpression [ Expression ]
...
3. Deje que propertyNameReference sea ​​el resultado de evaluar Expression .
4. Deje que propertyNameValue sea ? GetValue (propertyNameReference) .
6. Deje que sea propertyKey ? ToPropertyKey (propertyNameValue) .

Entonces, lo que está sucediendo aquí es que está accediendo a su matriz (MemberExpression) usando [] con una Expresión.

Para acceder con [] se evaluará la Expresión, y luego se llamará a GetValue. Entonces se llamará a ToPropertyKey.

  1. propertyNameReference = Evaluar expresión b = b
  2. propertyNameValue = GetValue (propertyNameReference) = ['foo']
  3. propertyKey = ToPropertyKey (propertyNameValue) = 'foo'

ToPropertyKey, en nuestra situación, conduce a ToPrimitive y luego a ToOrdinaryPrimitive que establece que deberíamos llamar "toString" en el argumento ( ['foo'] en nuestro caso).

Aquí es donde entra en juego la implementación. En el lado de la implementación,

El objeto Array anula el método toString de Object. Para los objetos de matriz, el método toString se une a la matriz y devuelve una cadena que contiene cada elemento de matriz separado por comas " MDN - Array toString

Cuando solo hay un valor en la matriz, el resultado será simplemente ese valor.

¿Cómo se puede prevenir esto?

Esta es la forma actual en que se implementa. Para cambiar eso, debe cambiar la implementación predeterminada, usar la detección para evitar la llamada o usar la guía para evitar la llamada.

Guia

Documente y aplique mecanismos de llamada en su código. Esto puede no ser siempre posible. Sin embargo, al menos es razonable esperar que los programadores no llamen al acceso a la propiedad con matrices.

Detección

Esto dependerá del entorno actual. Con la iteración más reciente de JavaScript, puede usar la aplicación de tipos para garantizar que el acceso a la propiedad sea Número o Cadena. La mecanografía lo hace bastante fácil ( aquí hay un buen ejemplo ). Esencialmente, solo requeriría que el acceso se defina como:

function ArrayAccess(value: string | number) {

y esto evitaría que cualquiera use la matriz como un valor de acceso.

Implementación predeterminada

Cambiar la implementación predeterminada es una idea terrible. Es más que probable que cause todo tipo de cambios importantes, y no debe hacerse. Sin embargo, solo para completar, así es como se vería. Principalmente, estoy mostrando esto para que pueda reconocerlo si lo ve en algún lugar y luego matarlo con fuego (o verifique algún código para solucionarlo si no hay arañas cerca de él).

var arrayToString = [].toString;
Array.prototype.toString = function(){
  if(this.length === 1) return;
  return arrayToString.call(this);
};

Cambiar la implementación de la instancia tampoco es una mejor idea. Eso está cubierto por @Code Maniac en una respuesta separada . "En escenarios prácticos, nunca debes hacer esto" afirma, con lo que también estoy de acuerdo.





javascript