Ответ 1
Я нашел способ обойти поведение IE8 с помощью неявной операции toString()
, а спецификация ECMAScript объясняет, почему обход имеет смысл. Неявным toString()
является следующее:
"" + window.localStorage
Это неявно принудительно вызывает вызов внутреннего метода toString()
объекта, и в IE это вернет желаемую форму, которую вы хотите [object Storage]
, и вы можете заставить свой код работать без специального корпуса window.localStorage
.
Итак, я искал минимальный риск, чтобы включить это в существующий код. Выбранный подход заключался в том, чтобы получить тип, который вы используете для его получения, и только тогда, когда он возвращает общий тип "Объект", а затем посмотрите, есть ли лучшее имя с новым методом. Таким образом, все, что было хорошо работает, будет продолжать работать так, как они делали, и мы могли бы найти лучшее имя для некоторых объектов (например, window.localStorage
), которые использовались для возврата общего имени "Объект". Еще одно изменение заключается в том, что я чувствовал себя менее уверенно в отношении точного типа возврата, который мы могли бы получить из конструкции "" + obj
, поэтому мне нужен метод синтаксического анализа, который бы не выдавал ошибку при непредвиденных данных, поэтому я переключился на регулярное выражение из раскола /replace, который вы использовали. Регулярное выражение также гарантирует, что это действительно формат [object Type]
, который кажется желательным.
Затем, чтобы защитить от нечетной проблемы сравнения localStorage === window
и получения ошибки, вы можете добавить проверку типа (утиный ввод), что объект, не похожий на окно, не пройдет, и это отфильтрует localStorage
выпуск и любые другие объекты с той же проблемой. В этом конкретном случае я уверен, что тип объекта "object"
и что он имеет свойство с именем setInterval
. Мы могли бы выбрать любое хорошо известное, хорошо поддерживаемое свойство объекта window
, которое вряд ли будет находиться на каком-либо другом объекте. В этом случае я использую setInterval
, потому что тот же тест, который использует jQuery, когда он хочет знать, является ли объект окном. Заметьте, я также изменил код, чтобы явно не сравнивать с window
вообще, потому что может быть более одного объекта window
(фреймы, iframes, popups и т.д.), Поэтому таким образом он вернет "Окно", для любого объекта окна.
Здесь код:
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
, поэтому он либо остается отдельным браузером, либо находится в каком-либо другом документе спецификации.
Далее, spec говорит об этом [[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]]
name, если тип объекта не реализует другое значение по умолчанию.
Итак, действительно, что этот новый код, который я предложил, делает через spec, это псевдо-код:
- Попробуйте получить внутреннюю переменную
[[Class]]
с помощьюObject.prototype.toString()
- Если это дает нам нечто иное, чем
"object"
, тогда используйте его - В противном случае используйте
"" + obj
, чтобы попытаться получить строчную версию[[DefaultValue]]
- Если это возвращает что-то полезное, используйте его
- Если у нас еще нет чего-то более полезного, чем
"object"
, просто верните"object"