7

我正在阅读 ES6 的草稿,并在以下Object.prototype.toString部分中注意到了这个注释:

历史上,此函数偶尔用于访问 [[Class]] 内部属性的字符串值,该属性在本规范的先前版本中用作各种内置对象的标称类型标记。toString 的这种定义保留了将其用作那些特定类型的内置对象的可靠测试的能力,但它没有为其他类型的内置或程序定义的对象提供可靠的类型测试机制。

通过阅读es-discuss 上的这个帖子,听起来好像在 ES6 草案[[Class]]中被替换为[[NativeBrand]],以便他们可以将其指定为不可扩展(至少是Allen Wirfs-Brock 的想法)。

好奇,我在 FireFox 和 Chrome 中进行了快速测试(启用了实验性 JavaScript):

Object.prototype.toString.apply(new WeakMap());
=> '[object WeakMap]'

"WeakMap"不是[[NativeBrand]]ES6 草案中指定的 s 之一。但是,此测试"[object WeakMap]"在两种浏览器上都返回。

所以我很困惑。我有几个问题。


1. Chrome 和 Firefox 的行为是否正确?

从阅读草稿的一种方式来看,它们听起来应该返回[object Object](所有这些都是相当新的,所以在这些浏览器的未来版本中看到这种变化我不会感到惊讶)。但是,我很难理解草案这一部分的意图,特别是因为有些地方带有"???".

有没有更热心关注es-discuss的人有相关信息?或者任何可以更好地理解草案语言的人?


2. 有替代品Object.prototype.toString吗?

从上面引用的注释来看,它听起来好像Object.prototype.toString是出于遗留原因而保留的,好像现在应该使用一些新的东西。尤其是节点中读取的部分"it does not provide a reliable type testing mechanism for other kinds of built-in ... objects"。这是否意味着未来的内置插件无法使用此方法进行测试?

让我们用一个具体的例子。

如果我想确保从未知来源收到的对象是一个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,那么我可以将其填入并从那里继续。谢谢!


更新

benvie的回答为我提供了正确的术语来搜索和理解我的问题的答案。

在 es-discuss 上找到了一封来自 Allen Wirfs-Brock 的关于此问题的电子邮件。

这是我发现的,对于其他提出相同问题的人:

1. Chrome 和 Firefox 的行为是否正确?

是的,为什么在下面解释。

2. 有替代品Object.prototype.toString吗?

就像现在一样,在可能性的意义上会有几个“替代品” ,但在替代的意义上不会。

一个。 使用@@toStringTag符号。但是,我的理解是Object.prototype.toString仍然应该使用它。@@toStringTag提供以允许扩展可以从Object.prototype.toString. 如果您有一个想要添加自己的字符串标签的原型,您可以使用@@toStringTag将值设置为任何字符串。Object.prototype.toString将返回此值,除非此值是 ES5 内置函数之一,在这种情况下,字符串标记将带有 '~' 前缀。

湾。 在用户定义的对象上使用私有符号。我读过一封电子邮件,宣传这是对用户定义的对象进行相同类型检查的最佳方式。但是,我看不出这如何真正解决问题,因为我无法理解它如何成为跨框架解决方案,并且它不允许您检查 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。

似乎确实提出了一个问题,为什么您不能拥有一个用户定义的对象,该对象报告在功能上等同于 a Stringor Array(没有前缀"~")。

4

1 回答 1

1

这目前是 ES6 规范中的一个移动目标。对于现有的对象集,出于各种原因(包括兼容性)维护现有机制。在10 月 26 日发布的最新 ES6 规范中,您可以找到一些关于潜在未来方向的提示

15.4.6.2.4 ArrayIterator.prototype.@@toStringTag
@@toStringTag 属性的初始值为字符串值“Array Iterator”。

15.14.5.13 Map.prototype.@@toStringTag
@@toStringTag 属性的初始值是字符串值“Map”。

您可以在 es-discuss 上的此线程中找到最初的讨论

于 2012-11-01T01:14:47.930 回答