jAn*_*ndy 13 javascript browser dom internet-explorer-8 local-storage
我最近在阅读和比较某些[[Class]]属性的值时遇到了IE8的问题(我现在还不知道9).实际上只有在localStorage对象的情况下.
我正在使用这样的方法
var ToStr = Object.prototype.toString;
Object.type = function _type( obj ) {
var res = ToStr.call( obj ).split( ' ' )[ 1 ].replace( ']', '' );
if( obj === window ) {
res = 'Window';
}
else if( res === 'Window' || res === 'Global' ) {
res = 'Undefined';
}
else if( res.indexOf( 'HTML' ) === 0 ) {
res = 'Node';
}
return ( res );
};
Run Code Online (Sandbox Code Playgroud)
此方法将返回此值,例如:
var foo = { },
bar = [ ],
num = 52,
win = window;
Object.type( foo ) === 'Object'; // true
Object.type( bar ) === 'Array'; // true
Object.type( num ) === 'Number'; // true
Object.type( win ) === 'Window'; // true
Run Code Online (Sandbox Code Playgroud)
这当然是有效的,在我所知道的所有浏览器中,只需[[Class]]从对象本身检查该属性即可.现在,我在localStorage对象上调用此方法
Object.type( win.localStorage ) === 'Storage' // true (not in IE8)
Run Code Online (Sandbox Code Playgroud)
IE8刚刚回到Object这里.但是,这不是实际问题,当您尝试将localStorage对象与window对象进行比较时会发生问题.如您所见,我正在检查传入的参数是否是当前window对象
if( obj === window ) { }
Run Code Online (Sandbox Code Playgroud)
如果obj现在是window.localStorage对象,则最终会出错
"Class does not support automation"
Run Code Online (Sandbox Code Playgroud)
只有在您尝试与之比较localStorage时才会发生这种情况window,您可以毫不费力地将其与其他任何内容进行比较.这只是另一个错误,还是我能以某种方式解决这个问题?
我想基本上我的问题是:
如果你正在处理这个localStorage对象,你怎么知道IE8(也可能是IE9)?
我想要做的最后一件事是使用a内部包装整个方法,try-catch因为它经常被调用.
在这里完全让我感到困惑的是:当你console.log( obj )在IE8的控制台中执行它时,它会返回你[object Storage](很好!),但如果你调用Object.prototype.toString.call( obj )它返回[object Object].同样typeof obj,将返回object.
第二个问题:
IE8如何console打印出正确的[[Class]]?
我找到了一种使用隐式toString()操作解决IE8行为的方法,而ECMAScript规范解释了为什么解决方法有意义.隐含的toString()是:
"" + window.localStorage
Run Code Online (Sandbox Code Playgroud)
这隐式强制调用对象的内部toString()方法,并且在IE中,这将返回所需的所需表单,[object Storage]并且您可以使代码无需特殊外壳即可工作window.localStorage.
所以,我一直在寻找将其融入现有代码的最小风险方式.选择的方法是获取与您使用相同方式获取它的类型,当且仅当它返回通用"对象"类型时,然后查看新方法是否有更好的名称.所以,过去工作得很好的所有东西都将继续按照它们的方式工作,我们可能会找到一些更好的名称window.localStorage,用于返回通用"对象"名称的某些对象(例如).另一个变化是我对从"" + obj构造中获得的确切返回类型感到不太自信,所以我想要一个不会在意外数据上引发错误的解析方法,所以我从split/replace方法切换到正则表达式你在用.正则表达式也强制执行它真正的[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 );
};
Run Code Online (Sandbox Code Playgroud)
请在此处查看包含各种测试用例的演示:http://jsfiddle.net/jfriend00/euBWV
"[object Storage]"为了解析"存储"类名,您所需的值来自ECMAScript规范中[[Class]]定义的内部属性.在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]]for对象通常是[[Class]](除非对象类型决定它有一个更好的默认值,Array显然这样做.)所以,我认为我们已经确认修复IE8的解决方案实际上应该工作因此,它不仅是IE8的解决方法,而且[[Class]]如果对象类型没有实现不同的默认值,它也是获取名称的替代路径.
那么,我提出的这个新代码通过规范实现的是这个伪代码:
[[Class]]使用内部变量Object.prototype.toString() "Object",请使用它"" + obj尝试获取字符串版本[[DefaultValue]]"Object",那就回去吧"Object"