我找到了一种使用隐式方法来解决 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
对象(框架、iframe、弹出窗口等),这样,它将为任何窗口对象返回“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 http://jsfiddle.net/jfriend00/euBWV
的期望值"[object Storage]"
您为了解析出“Storage”类名而进行的操作来自内部[[Class]]
中定义的属性ECMAScript 规范 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf。在第 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]
or [object String]
通过使用[[Class]
作为第二个词。
So, Object.prototype.toString
这就是它应该如何工作的。显然IE8在这方面存在buglocalStorage
目的。我们无法知道 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 中对 string 的类型强制最终给了我们[[Class]]
我们想要的。
作为一个有趣的测试,我在 Chrome 浏览器中尝试了我的测试套件,运行所有作为对象的测试用例"" + obj
解决方法(通常代码仅在以下情况下使用该路径)Object.prototype.toString()
不返回除以下名称之外的名称"Object"
。它适用于除数组之外的所有内容。我认为这意味着[[DefaultValue]]
对于物体来说一般是[[Class]]
(除非对象类型决定它有一个更好的默认值Array
显然是这样)。因此,我认为我们已经确认修复 IE8 的解决方法实际上应该按照规范工作。因此,它不仅是 IE8 的解决方法,而且是访问[[Class]]
name 如果对象类型没有实现不同的默认值。
所以,我提出的这个新代码实际上是通过规范执行的伪代码:
- 尝试获取内部变量
[[Class]]
using Object.prototype.toString()
- 如果这给我们带来了除了
"Object"
然后,使用它
- 否则,使用
"" + obj
尝试获取字符串版本[[DefaultValue]]
- 如果返回有用的东西,请使用它
- 如果我们仍然没有比这更有用的东西
"Object"
,然后返回"Object"