Access [[NativeBrand]] / [[Class]] in ES6 (ECMAScript 6)

元气小坏坏 提交于 2020-01-02 02:56:06

问题


I was reading over the draft for ES6, and I noticed this note in the Object.prototype.toString section:

Historically, this function was occasionally used to access the string value of the [[Class]] internal property that was used in previous editions of this specification as a nominal type tag for various built-in objects. This definition of toString preserves the ability to use it as a reliable test for those specific kinds of built-in objects but it does not provide a reliable type testing mechanism for other kinds of built-in or program defined objects.

From reading this thread on es-discuss, it sounds like [[Class]] is being replaced with [[NativeBrand]] in the ES6 draft so that they can specify it as being non-extensible (those were at least Allen Wirfs-Brock's thoughts).

Curious, I ran a quick test in FireFox and Chrome (with experimental JavaScript enabled):

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

"WeakMap" is not one of the [[NativeBrand]]s specified in the ES6 draft. However, this test returned "[object WeakMap]" on both browsers.

So I'm confused. I have a few questions.


1. Do Chrome and Firefox behave correctly?

From one way of reading the draft it sounds like they should return [object Object] (and all of this is pretty new, so I wouldn't be surprised to see this change in future editions of these browsers). However, it's hard for me to understand the intention of this section of the draft, especially since there are some places with "???".

Does anyone who has been following es-discuss more fervently have any relevant information? Or anyone who can understand the draft language better?


2. Is there an alternative to Object.prototype.toString?

From the note quoted above it makes it sound as if Object.prototype.toString is retained for legacy reasons, as if there's something new now that should be used instead. Especially the part of the node that reads "it does not provide a reliable type testing mechanism for other kinds of built-in ... objects". Does that mean that future built-ins can't be tested with this method?

Let's use a concrete example.

If I want to ensure an object I have received from an unknown source is a String object (an actual constructed String object, not a primitive string), I could do:

if (Object.prototype.toString.apply(unknownObject) != '[object String]')
    throw new TypeError('String object expected.');

This lets me know if unknownObject is a String object no matter what frame it was constructed in.

My question is, should this be the approach I take moving forward into ES6? Or is there an alternative? Something like Object.getNativeBrandOf?


3. Since [[NativeBrand]] seems like it won't include future types of objects, how would one test for these objects?

Will this work?

if (Object.prototype.toString.apply(unknownObject) != '[object Symbol]')
    throw new TypeError('Symbol expected.');

...assuming Symbol is the eventual name for Private Names.

Should I use this?

if (Object.prototype.toString.apply(unknownObject) != '[object WeakMap]')
    throw new TypeError('WeakMap expected.');

... or something else?


The reason I ask is I am currently writing code that I want to be able to transition as easily as possible to ES6 in a year or two when possible. If there is a replacement for Object.prototype.toString, then I can just shim it in and continue from there. Thanks!


Update

benvie's answer provided me with the correct term to search for and understand the answer to my questions.

I found an email from Allen Wirfs-Brock on es-discuss concerning this issue.

Here's what I found, for anyone else asking the same questions:

1. Do Chrome and Firefox behave correctly?

Yes, why is explained below.

2. Is there an alternative to Object.prototype.toString?

As it is now, there will be a couple "alternatives" in the sense of possibilities, but not in the sense of replacements.

a. Using the @@toStringTag symbol. However, my understanding is that Object.prototype.toString should still probably be used. @@toStringTag is provided to allow extending the results that can be returned from Object.prototype.toString. If you have a prototype you would like to add your own string tag to, you could use @@toStringTag to set the value to any string. Object.prototype.toString will return this value except in the case where this value is one of the ES5 built-ins, in which case the string tag will be prepended with '~'.

b. Using private symbols on user-defined objects. I read one email promoting this as the best way to do the same type of check on a user-defined object. However, I don't see how that really settles the issue, as I fail to understand how it could be a cross-frame solution and it doesn't let you check against ES6 built-ins.

So even though there are some alternatives, it's good to stick with Object.prototype.toString now and going forward, with one caveat:

It'll work to make sure you have an ES5 built-in, such as String, but it won't be fool-proof to make sure you have an ES6 built-in because they can be spoofed with @@toStringTag. I'm not sure why this is, and I may be missing something, or it could change as the spec evolves.

3. Since [[NativeBrand]] seems like it won't include future types of objects, how would one test for these objects?

As mentioned above, Object.prototype.toString can still be used on ES6 built-ins, but it's not fool-proof as it can be spoofed by anyone with access to the @@toStringTag symbol. However, maybe there shouldn't be a fool-proof method, since Object.prototype.toString(weakmap) == '[object WeakMap]' doesn't mean that weakmap instanceof WeakMap (and it shouldn't!). The weakmap could have come from another frame, or it could be a user-created weakmap-like object. The only thing you really know is it reports to be functionally equivalent to a WeakMap.

It does seem to beg the question why you can't have a user-defined object which reports to be functionally equivalent to a String or Array (sans the prefixed "~").


回答1:


This is currently a moving target in the ES6 spec. For the existing set of objects, the existing mechanics are maintained for various reasons including compatibility. In the most recent ES6 spec, published October 26th, you can find some hints of the potential future direction

15.4.6.2.4 ArrayIterator.prototype.@@toStringTag
The initial value of the @@toStringTag property is the string value "Array Iterator".

15.14.5.13 Map.prototype.@@toStringTag
The initial value of the @@toStringTag property is the string value "Map".

You can find the original discussion that originated this in this thread on es-discuss



来源:https://stackoverflow.com/questions/13151643/access-nativebrand-class-in-es6-ecmascript-6

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!