奇怪的IE8内部[[class]]属性行为

时间:2012-03-26 12:25:20

标签: javascript dom browser 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 );
};

此方法将返回此值,例如:

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

当然,在我所知道的所有浏览器中,只需从对象本身检查[[Class]]属性即可。现在,我在localStorage对象

上调用此方法
Object.type( win.localStorage ) === 'Storage' // true (not in IE8)

IE8只返回Object。但是,这不是实际问题,当您尝试将localStorage对象与window对象进行比较时,会出现问题。如您所见,我正在检查传入的参数是否是当前的window对象

if( obj === window ) { }

如果obj现在是window.localStorage对象,则会出现错误

"Class does not support automation"

只有在您尝试将localStoragewindow进行比较时才会发生这种情况,您可以将其与其他任何内容进行比较而不会有任何问题。这只是另一个错误,还是我能以某种方式解决这个问题?

我想基本上我的问题是:

如果你正在处理localStorage对象,你怎么知道IE8(也可能是IE9)?

我要做的最后一件事是用try-catch内部包装整个方法,因为它经常被调用。

完全让我感到困惑的是:当你在IE8的控制台中执行console.log( obj )时,它会返回[object Storage](很好!)但是如果你调用Object.prototype.toString.call( obj )它会返回[object Object] }。同样适用于typeof obj,将返回object

第二个问题:

IE8 console如何打印出正确的[[Class]]

5 个答案:

答案 0 :(得分:6)

我找到了一种使用隐式toString()操作解决IE8行为的方法,ECMAScript规范解释了为什么解决方法有意义。隐含的toString()就是这样:

"" + window.localStorage

这隐含地强制调用对象的内部toString()方法,并且在IE中,这将返回您想要的[object Storage]所需的表单,并且您可以使代码无需工作特殊套管window.localStorage

所以,我一直在寻找将其融入现有代码的最小风险方式。选择的方法是获取与您使用相同方式获取它的类型,当且仅当它返回泛型" Object"键入,然后查看新方法是否有更好的名称。所以,过去工作得很好的所有东西都将继续以他们的方式工作,我们可能会找到一些更好的名称,用于返回通用"对象"的某些对象(如window.localStorage)。名称。另一个变化是我对从"" + obj构造得到的确切返回类型感到不太自信,所以我想要一个不会在意外数据上抛出错误的解析方法,所以我切换到了来自您使用的拆分/替换方法的正则表达式。正则表达式也强制它实际上也是[object Type]格式,这似乎是可取的。

然后,为了防止比较localStorage === window和获取错误的奇怪问题,您可以添加类型检查(鸭子类型)非类似窗口的对象不会通过,这将过滤掉{ {1}}问题和具有相同问题的任何其他对象。在这种特殊情况下,我确保对象的类型为localStorage,并且它具有名为"object"的属性。我们可以选择setInterval对象的任何众所周知的,支持良好的属性,该属性不可能出现在任何其他对象上。在这种情况下,我使用window,因为它与jQuery在想知道对象是否是窗口时使用的测试相同。注意,我还将代码更改为未明确与setInterval进行比较,因为可能有多个window对象(框架,iframe,弹出窗口等等),所以这样,它会返回" Window"对于任何窗口对象。

以下是代码:

window

在此处查看包含各种测试用例的演示:http://jsfiddle.net/jfriend00/euBWV

您为了解析"存储"所需的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 ); }; 值。类名来自ECMAScript spec中定义的内部"[object Storage]"属性。在8.6.2节中,规范定义了[[Class]]的特定类名。它没有为"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String"这样的主机对象定义类名,因此可以将其留给单个浏览器,也可以在其他规范文档中找到。

此外,规范说明了localStorage

  

[[Class]]内部属性的值在内部使用   区分不同种类的物体。注意这个规范   没有为程序提供任何访问该值的方法,除非   通过Object.prototype.toString(见15.2.4.2)。

并且,在15.2.4.2中,我们通过使用[[Class]]作为第二个单词找到了生成类似[object Array][object String]的输出的规范。

所以,[[Class]是应该如何工作的。显然,IE8在这方面存在对Object.prototype.toString对象的错误。我们无法在IE8内部了解localStorage是否未使用toString()[[Class]]未正确设置。在任何情况下,IE8中的[[Class]]似乎都没有直接使用console.log(),因为它会产生不同的结果。

Object.prototype.toString()解决方法的行为更加难以理解。该规范描述了如何将对象的类型强制转换为字符串。在整个规范中一直遵循线程有点复杂,因为一部分依赖于另一部分依赖于另一部分,依此类推。但是,最后,它执行内部方法"" + obj,显然在IE8中ToString(ToPrimitive(input argument, hint String)),当传递一个提示,我们想要一个字符串给我们ToPrimitive不是的实际类名。有一条通过Object.prototype.toString()的规范的路径,这可能是IE8中发生的情况,但是因为我们已经知道IE8没有遵循规范的第一部分而且一般都没有不管怎样,擅长遵循规范,假设它遵循这方面的规范并不是一个有效的假设。最后,我们只知道在IE8中对字符串进行类型强制最终会向我们提供我们想要的[[DefaultValue]]

作为一项有趣的测试,我在Chrome浏览器中尝试了我的测试套件,通过[[Class]]解决方案运行作为对象的所有测试用例(通常代码仅在"" + obj无法使用时使用该路径#39; t返回Object.prototype.toString()以外的名称。它适用于除数组之外的所有内容。我认为这意味着对象的"Object"通常为[[DefaultValue]](除非对象类型决定它有一个更好的默认值[[Class]]显然是这样做的。所以,我认为我们已经确认修复IE8的解决方案实际上应该按照规范工作。因此,它不仅是一个解决方案IE8,但如果对象类型没有实现不同的默认值,它是获取Array名称的替代路径。

所以,我通过规范提出的新代码真的是这个伪代码:

  1. 尝试使用[[Class]]
  2. 获取内部变量[[Class]]
  3. 如果这给了我们Object.prototype.toString()以外的其他内容,请使用
  4. 否则,请使用"Object"尝试获取"" + obj
  5. 的字符串版本
  6. 如果返回有用的内容,请使用
  7. 如果我们仍然没有比[[DefaultValue]]更有用的东西,那么只需返回"Object"

答案 1 :(得分:3)

您写道:

  

只有在您尝试将localStoragewindow进行比较时才会发生这种情况,您可以毫不费力地将其与其他任何内容进行比较。

那你为什么不这样做?

var ToStr = Object.prototype.toString; 
Object.type = function _type( obj ) { 
    if ( window.localStorage && obj === window.localStorage )
        return 'Storage';
    if ( obj === window ) 
        return 'Window'; 
    var res = ToStr.call( obj ).split( ' ' )[ 1 ].replace( ']', '' ); 
    if ( res === 'Window' || res === 'Global' ) { 
        return 'Undefined'; 
    if ( res.indexOf( 'HTML' ) === 0 ) {  
        return 'Node'; 
    return res; 
}; 

除了直接回答问题外:

  1. “......或者我可以以某种方式解决这个问题吗?”:你做不到。如果浏览器有比较两个特殊值的错误,则无法在javascript代码中修复此问题....
  2. “如果你正在处理localStorage对象,你怎么知道IE8(也可能是IE9)?”如果您正在处理它,只需比较obj === window.localStorage即可查看。它不能比那更简单,可以吗?
  3. “IE8控制台如何打印出正确的[[Class]]?”内部函数对这些对象有一种非常不同的访问方式,比javascript有......你不能在那里做同样的事情。
  4. 此致   斯特芬

答案 2 :(得分:1)

应使用功能检测(见下文),以避免尝试访问失败localStorage due to browser policies etc.

至于IE8特定的问题,您是否可以确认该网页正在提供而不是在本地打开?即URL为http://localhost/hello.html而不是file:///C:/somefolder/hello.html IE8不允许localStorage本地打开的文件,但是没有找到官方文档来支持这个(但是thisthis :)另外,可能值得检查一下你是不是在IE7模式下运行浏览器。

如果上面的代码应该检测到功能可用性而不是其他东西,那么可以选择使用the following之类的代码:

// Feature test
var hasStorage = (function() {
  try {
    localStorage.setItem(mod, mod);
    localStorage.removeItem(mod);
    return true;
  } catch(e) {
    return false;
  }
}());

答案 3 :(得分:0)

这根本不是很好。我能获得字符串"[object Storage]"的唯一方法是使用以下代码:

obj.constructor.toString();

对于任何本机构造函数,其他浏览器中的输出和预期输出是本机函数的字符串表示形式。但是,我们在这里讨论的是主机对象,其中完全适用不同的规则。有趣的是,尽管对DOM进行了所有改进,IE 9也给出了相同的结果。

这不是一个完全有弹性的解决方案,但它是我能在短时间内找到解决问题的唯一解决方案。


似乎IE 8和IE 9的IE 8文档模式之间存在差异,localStorage.constructor实际上并不存在于前者中。在这种情况下,我认为没有另一种可行的解决方案。鸭子打字似乎不会有效,因为所有localStorage对象的属性名称都是通用的。你可以使用

window.localStorage === obj

但是当我试图覆盖窗口对象的本机属性时,我不确定IE 8的行为(如果它不允许它,那么你可能没问题。)

答案 4 :(得分:0)

这确实是一个非常奇怪的IE8错误! (哦,我喜欢IE浏览器)!

正如IE8浏览器的评论中所述,我认为只有一种解决方案:

typeof obj === "object" && obj.constructor.toString() === "[object Storage]"

确保当前浏览器在检查之前是IE8,否则不工作,甚至在其他版本的IE中也不行!

P.S。 IE与其兄弟姐妹的兼容性的一个很好的例子!