Доступ к [[NativeBrand]] / [[Class]] в ES6 (ECMAScript 6)

Я читал черновик для ES6 и заметил это примечание в разделе Object.prototype.toString:

Исторически эта функция иногда использовалась для доступа к строковому значению внутреннего свойства [[Class]], которое использовалось в предыдущих редакциях этой спецификации в качестве тега номинального типа для различных встроенных объектов. Это определение toString сохраняет возможность использовать его в качестве надежного теста для определенных видов встроенных объектов, но не обеспечивает надежного механизма проверки типов для других типов встроенных или определяемых программой объектов.

Судя по этой ветке на es-discuss, это звучит например, [[Class]] заменяется на [[NativeBrand]] в проекте ES6, чтобы они могли указать его как нерасширяемый (это было как минимум мысли Аллена Вирфс-Брока).

Любопытно, я провел быстрый тест в FireFox и Chrome (с включенным экспериментальным JavaScript):

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

"WeakMap" не является одним из [[NativeBrand]], указанных в проекте ES6. Однако этот тест вернул "[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, то я могу просто вставить ее и продолжить оттуда. Спасибо!


Обновлять

Ответ бенви предоставил мне правильный термин для поиска и понимания ответов на мои вопросы.

Я нашел электронное письмо от Аллена Вирфс-Брока на 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.

Кажется, возникает вопрос, почему вы не можете иметь пользовательский объект, который сообщает, что он функционально эквивалентен String или Array (без префикса "~").


person Nathan Wall    schedule 31.10.2012    source источник
comment
Ответ на самый последний вопрос (о ~ и т. д.) заключается в том, что в Интернете существует код, который зависит от существующих значений результатов O.p.toString, которые нельзя подделать для существующих встроенных модулей ES5. Мы хотим сохранить эту гарантию, но только для объектов/значений [[Class]], о которых O.p.toString знал в ES‹=5.   -  person Allen Wirfs-Brock    schedule 03.11.2012
comment
Шаг ~ был удален в версии 32.   -  person Knu    schedule 16.03.2015
comment
Это позволяет мне узнать, является ли unknownObject строковым объектом, независимо от того, в каком фрейме он был построен. Не в Opera.   -  person Knu    schedule 12.10.2015
comment
В ecma-international.org/ecma-262/6.0 ES6 Стандарт. Я не нашел NativeBrand, поэтому полагаю, что class не заменяется на NativeBrand.   -  person Mocuishle    schedule 28.04.2018


Ответы (1)


В настоящее время это движущаяся цель в спецификации ES6. Для существующего набора объектов существующая механика сохраняется по разным причинам, включая совместимость. В самой последней спецификации ES6 опубликовано 26 октября, вы можете найти некоторые намеки на потенциальное будущее направление

15.4.6.2.4 ArrayIterator.prototype.@@toStringTag
Начальным значением свойства @@toStringTag является строковое значение "Array Iterator".

15.14.5.13 Map.prototype.@@toStringTag
Начальным значением свойства @@toStringTag является строковое значение "Map".

Вы можете найти исходное обсуждение, которое породило это в этой теме на es-обсудить

person Community    schedule 01.11.2012
comment
Спасибо, что направили меня в правильном направлении и дали мне нужный термин. Я обновил свой вопрос более полным ответом выше для потомков. - person Nathan Wall; 01.11.2012