Я нашел способ обойти поведение IE8, используя неявную операцию toString()
, и спецификация ECMAScript объясняет, почему обходной путь имеет смысл. Неявное toString()
таково:
"" + window.localStorage
Это неявное принудительное обращение к внутреннему методу toString()
объекта, и в IE это вернет желаемую форму, которую вы хотите [object Storage]
, и вы сможете заставить свой код работать без специального регистра window.localStorage
.
Итак, я искал способ минимального риска включить это в существующий код. Выбранный подход состоял в том, чтобы получить тип тем же способом, который вы используете для его получения, и тогда и только тогда, когда он возвращает универсальный тип «Объект», тогда посмотрите, есть ли лучшее имя, доступное с новым методом. Итак, все вещи, которые раньше работали просто отлично, будут продолжать работать так же, как и они, и мы могли бы найти лучшее имя для некоторых объектов (например, window.localStorage
), которые раньше возвращали общее имя «Объект». Еще одно изменение заключается в том, что я чувствовал себя менее уверенно относительно точного типа возврата, который мы могли бы получить из конструкции "" + obj
, поэтому я хотел использовать метод синтаксического анализа, который бы не выдавал ошибку при непредвиденных данных, поэтому я переключился на регулярное выражение из разбиения. / заменить метод, который вы использовали. Регулярное выражение также подтверждает, что это действительно формат [object Type]
, который кажется желательным.
Затем, чтобы защитить от нечетной проблемы сравнения localStorage === window
и получения ошибки, вы можете добавить проверку типа (утка), что объект, не похожий на окно, не пройдет, и это отфильтрует localStorage
выпускать и любые другие объекты с той же проблемой. В этом конкретном случае я удостоверяюсь, что тип объекта - "object"
и что у него есть свойство с именем setInterval
. Мы могли бы выбрать любое хорошо известное, хорошо поддерживаемое свойство объекта window
, которое вряд ли будет на любом другом объекте. В этом случае я использую setInterval
, потому что это тот же тест, который использует jQuery, когда хочет узнать, является ли объект окном. Обратите внимание, я также изменил код, чтобы он вообще не сравнивался с window
, потому что может быть более одного объекта window
(фреймы, фреймы, всплывающие окна и т. Д.), Поэтому он вернет «Окно» для любого оконного объекта.
Вот код:
Object.type = function _type( obj ) {
function parseType(str) {
var split = str.split(" ");
if (split.length > 1) {
return(split[1].slice(0, -1));
}
return("");
}
var res = parseType(Object.prototype.toString.call(obj));
// if type is generic, see if we can get a better name
if (res === "Object") {
res = parseType("" + obj);
if (!res) {
res = "Object";
}
}
// protect against errors when comparing some objects vs. the window object
if(typeof obj === "object" && "setInterval" in obj) {
res = 'Window';
}
else if( res === 'Window' || res === 'Global' ) {
res = 'Undefined';
}
else if( res.indexOf( 'HTML' ) === 0 ) {
res = 'Node';
}
return ( res );
};
Смотрите демонстрацию с различными тестовыми примерами здесь: http://jsfiddle.net/jfriend00/euBWV
Желаемое значение "[object Storage]"
, которое вы указали после анализа имени класса «Хранилище», исходит из внутреннего свойства [[Class]]
, как определено в спецификации ECMAScript . В разделе 8.6.2 спецификация определяет конкретные имена классов для "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String"
. Он не определяет имена классов для хост-объектов, таких как localStorage
, поэтому он либо оставляется на усмотрение отдельных браузеров, либо находится в каком-либо другом документе спецификации.
Кроме того, спецификация говорит это о [[Class]]
:
Значение внутреннего свойства [[Class]] используется внутренне для
различать разные виды предметов. Обратите внимание, что эта спецификация
не предоставляет средств для доступа программы к этому значению, кроме
через Object.prototype.toString (см. 15.2.4.2).
И именно в 15.2.4.2 мы находим спецификацию для генерирования вывода, например [object Array]
или [object String]
, используя [[Class]
в качестве второго слова.
Итак, Object.prototype.toString
- это то, как это должно работать. Очевидно, что IE8 имеет ошибки в этом отношении для localStorage
объекта. Мы не можем знать, в IE8, toString()
не использует [[Class]]
или [[Class]]
установлен неправильно. В любом случае, похоже, что console.log()
в IE8 напрямую не использует Object.prototype.toString()
, потому что он генерирует другой результат.
Поведение обходного пути "" + obj
более сложное для понимания.Спецификация описывает, как предполагается приведение типа объекта к строке.Немного сложно проследить весь поток спецификации, поскольку одна часть зависит от другой, которая зависит от другой, и так далее.Но, в конце концов, он выполняет внутренние методы ToString(ToPrimitive(input argument, hint String))
и, по-видимому, в IE8, ToPrimitive
, когда передается намек на то, что нам нужна строка, дает нам фактическое имя класса, которого нет Object.prototype.toString()
.Существует путь через спецификацию, которая проходит через [[DefaultValue]]
, что может быть, как это происходит в IE8, но, поскольку мы уже знаем, что IE8 не следовал первой части спецификации, и в целом он не всегда хорошо следовал спецификации, это неверное предположение, чтобы предположить, что это соответствует спецификации в этом отношении.В конце концов, мы просто знаем, что приведение типов к строке в IE8 в итоге дает нам [[Class]]
, который мы хотели.
В качестве интересного теста я попробовал свой набор тестов в браузере Chrome, запустив всетестовые случаи, которые являются объектами через "" + obj
обходной путь (обычно код использует этот путь только тогда, когда Object.prototype.toString()
не возвращает имя, отличное от "Object"
. Это работает для всего, кроме массива. Я думаю, это означает, что[[DefaultValue]]
для объектов обычно [[Class]]
(если только тип объекта не решит, что он имеет лучшее значение по умолчанию, которое, по-видимому, Array
имеет). Итак, я думаю, у нас есть подтверждение того, что обходной путь, исправляющий IE8, фактически долженработа по спецификации. Таким образом, это не только обходной путь для IE8, но и альтернативный путь для получения имени [[Class]]
, если тип объекта не реализует другое значение по умолчанию.
Итак, действительно, этот новый код, который я предложил делать через спецификацию, это псевдокод:
- Попробуйте получить внутреннюю переменную
[[Class]]
, используя Object.prototype.toString()
- Если это дает нам что-то отличное от
"Object"
, тогда используйте это - В противном случае используйте
"" + obj
, чтобы попытаться получить строковую версию [[DefaultValue]]
- Если это возвращает что-то полезное, используйте это
- Если у нас все еще нет чего-то более полезного, чем
"Object"
, тогда просто верните "Object"