Object.prototype.toString
部分中有这样一条注释:历史上,此函数偶尔被用于访问[[Class]]内部属性的字符串值,该属性在本规范的先前版本中用作各种内置对象的名义类型标记。toString的这个定义保留了将其用作可靠测试特定类型的内置对象的能力,但对于其他类型的内置或程序定义对象,它不提供可靠的类型测试机制。
从阅读es-discuss上的this thread可以得知,听起来像是在ES6草案中将
[[Class]]
替换为[[NativeBrand]]
,以便他们可以将其指定为不可扩展的(至少Allen Wirfs-Brock's thoughts是这样的)。好奇心驱使下,我在FireFox和Chrome(启用实验性JavaScript)上进行了快速测试:
Object.prototype.toString.apply(new WeakMap());
=> '[object WeakMap]'
"
"WeakMap"
不是ES6草案中指定的[[NativeBrand]]
之一。然而,这个测试在两个浏览器上都返回了"[object WeakMap]"
。""所以我很困惑。我有几个问题。"
1. Chrome和Firefox表现正常吗?
从某种程度上来看,根据草案的一种阅读方式,它们应该返回[object Object]
(所有这些都是相当新的,所以我不会对这在未来的浏览器版本中发生变化感到意外)。然而,我很难理解草案这部分的意图,特别是因为有一些地方有"???"
。
有没有一直关注es-discuss的人有相关信息?或者有谁能更好地理解草案语言?
2. 是否有替代 Object.prototype.toString
的方法?
从上面引用的注释中可以看出,Object.prototype.toString
似乎是为了保留向后兼容性而保留的,好像现在有一些新的东西应该被代替。特别是那部分读作 "它不提供可靠的类型测试机制,用于其他类型的内置...对象"
。这是否意味着未来的内置对象无法使用此方法进行测试?
让我们举一个具体的例子。
如果我想确保从未知来源接收到的对象是一个 String
对象(一个实际构造的 String
对象,而不是一个原始字符串),我可以执行以下操作:
if (Object.prototype.toString.apply(unknownObject) != '[object String]')
throw new TypeError('String object expected.');
这让我知道无论在哪个框架中构建,
unknownObject
是否为String
对象。
我的问题是,这是否应该是我进入ES6时采取的方法?还是有其他选择?例如Object.getNativeBrandOf
?
3. 由于[[NativeBrand]]
似乎不包括未来的对象类型,那么如何测试这些对象?
这个方法可行吗?
if (Object.prototype.toString.apply(unknownObject) != '[object Symbol]')
throw new TypeError('Symbol expected.');
假设
Symbol
是私有名称的最终名称。
我应该使用它吗?
if (Object.prototype.toString.apply(unknownObject) != '[object WeakMap]')
throw new TypeError('WeakMap expected.');
...还是其他什么?
我询问的原因是,我正在编写代码,并希望在一两年后尽可能轻松地转换为ES6。如果有
Object.prototype.toString
的替代方法,那么我可以只需添加一个shim并继续进行。谢谢!
更新
benvie的回答为我提供了正确的术语,让我理解了我的问题的答案。
我找到了关于这个问题的一封来自Allen Wirfs-Brock在es-discuss邮件。
以下是我为任何其他提出相同问题的人所找到的内容:
1. Chrome和Firefox是否表现正确?
是的,为什么会在下面解释。
2. 是否有替代Object.prototype.toString
的方法?
现在有一些“替代方案”可以作为可能性,但不能作为替代品。
a. 使用@@toStringTag
符号。然而,我的理解是应该仍然使用Object.prototype.toString
。提供@@toStringTag
是为了允许扩展可以从Object.prototype.toString
返回的结果。如果您有一个原型想要添加自己的字符串标签,可以使用@@toStringTag
将值设置为任何字符串。Object.prototype.toString
将返回此值,除非此值是ES5内置之一,在这种情况下,字符串标记将以“~”开头。
b. 在用户定义的对象上使用私有符号。我读过一封电子邮件,推广这种方法作为对用户定义对象进行相同类型检查的最佳方式。然而,我不明白它如何成为跨框架解决方案,也不能让您检查ES6内置。
因此,即使有一些替代方案,现在和未来都坚持使用Object.prototype.toString
是好的,但有一个注意点:
这将确保您拥有ES5内置对象(例如String
),但它不能完全保证您拥有ES6内置对象,因为它们可以使用@@toStringTag
进行欺骗。我不确定为什么会这样,可能是我遗漏了一些东西,或者随着规范的发展可能会改变。
3. 由于[[NativeBrand]]
似乎不包括未来类型的对象,那么如何测试这些对象呢?
如上所述,Object.prototype.toString
仍然可以用于ES6内置对象,但它并不是完全可靠的,因为任何人都可以使用@@toStringTag
符号进行欺骗。但是,也许不应该有一个完全可靠的方法,因为Object.prototype.toString(weakmap) == '[object WeakMap]'
并不意味着weakmap instanceof WeakMap
(也不应该!)。weakmap
可能来自另一个框架,或者它可能是用户创建的类WeakMap对象。您唯一知道的是它声称在功能上等同于WeakMap。
这似乎引出了一个问题,为什么不能有一个用户定义的对象,它报告自己在功能上等同于一个String
或Array
(没有前缀"~"
)。
~
步骤。 - Knu