当用作对象键时,JavaScript为什么将一个字符串的数组转换为字符串?




(3)

我遇到了JavaScript表现有些令人困惑的情况。

假设我们有一个带有两个键foo和bar的对象。

a = { foo: 1, bar: 2 }

然后,我有一个字符串数组,在这种情况下是一个'foo'

b = ['foo']

我期望以下几点:

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

但是,这是发生了什么:

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

为什么JavaScript用作键时会转换 ['foo'] -> 'foo'

外面有人知道原因吗?

如何预防?

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


当使用数组作为键时,javascript会调用该数组的“ toString()”方法,然后尝试查找该数组的 字符串化 版本作为键。 而且,如果调用 ['foo'].toString() 则会看到此方法返回 "foo"


当使用数组作为键时,javascript会调用该数组的“ toString()”方法,然后尝试查找该数组的字符串化版本作为键。 而且,如果您调用['foo']。toString(),则会看到此方法返回“ foo”。


为什么JavaScript用作键时会转换['foo']->'foo'?
外面有人知道原因吗?

任何时候都有关于JavaScript为什么以一种意想不到的方式行事的困惑,然后查看语言定义是准确找出发生了什么的必经之路。

https://www.ecma-international.org/ecma-262/10.0/ 是发布此语言时的最新语言定义。

首先,您将要查找与阵列访问有关的区域。 虽然这是用语言表达的。

12.3.2.1运行时语义:评估
MemberExpression:MemberExpression [ 表达式 ]
...
3.让 propertyNameReference 为计算 Expression 的结果。
4.让 propertyNameValue GetValue(propertyNameReference)
6.让 propertyKey ToPropertyKey(propertyNameValue)

因此,这里发生的是您正在使用带有表达式的 [] 访问数组(MemberExpression)。

为了使用 [] 进行访问,将对表达式进行求值,然后将调用GetValue。 然后将调用ToPropertyKey。

  1. propertyNameReference =评估表达式 b = b
  2. propertyNameValue = GetValue(propertyNameReference)= ['foo']
  3. propertyKey = ToPropertyKey(propertyNameValue)= 'foo'

在我们的情况下, ToPrimitive 导致 ToPrimitive ,然后导致 ToPrimitive ,后者指出我们应该在参数上调用“ toString”(在本例中为 ['foo'] )。

这是实现发挥作用的地方。 在实施方面,

Array对象覆盖Object的toString方法。 对于Array对象,toString方法连接该数组并返回一个字符串,其中包含用逗号分隔的每个数组元素。“ MDN-Array toString

当数组中只有一个值时,结果将只是该值。

如何预防?

这是当前的实现方式。 为了更改此设置,您必须更改默认实现,使用检测阻止呼叫或使用指导阻止呼叫。

指导

在代码中记录并实施调用机制。 这并非总是可能的。 至少可以期望程序员不要调用数组的属性访问。

发现

这将取决于当前环境。 在JavaScript的最新版本中,您可以使用类型强制来确保属性访问为Number或String。 使用Typescript可以轻松完成此操作( 这是一个很好的示例 )。 从本质上讲,只需将访问权限定义为:

function ArrayAccess(value: string | number) {

这将阻止任何人将数组用作访问器值。

默认实施

更改默认实现是一个糟糕的主意。 它很可能会引起各种重大变化,因此不应该这样做。 但是,仅出于完整性考虑,这就是它的样子。 首先,我正在显示此图像,因此,如果您在某个地方看到它,则可以希望将其识别出来,然后用火将其杀死(或者如果附近没有蜘蛛,请检入一些代码进行修复)。

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

更改实例实现也不是一个更好的主意。 @Code Maniac在单独的答案中对此进行了介绍 。 指出:“在实际情况下,您永远都不要这样做”,我也同意。





javascript