草庐IT

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

coder 2024-05-10 原文

我最近在阅读和比较一些 [[Class]] 的值时遇到了一些问题(我现在不知道 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"

只有当您尝试比较 localStorage 时才会发生这种情况。与 window ,您可以毫不费力地将其与其他任何东西进行比较。这只是另一个错误还是我可以以某种方式解决此问题?

我想基本上我的问题是:

如果您正在处理 localStorage,您如何知道在 IE8(也可能是 IE9)中目的?

我想做的最后一件事是用 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

这隐含地强制调用对象的内部 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

为了解析出“存储”类名,您想要的 "[object Storage]" 值来自 ECMAScript spec 中定义的内部 [[Class]] 属性。在第 8.6.2 节中,规范为 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String" 定义了特定的类名。它没有为像 localStorage 这样的主机对象定义类名,因此它要么留给单个浏览器,要么在其他一些规范文档中找到。

此外,规范说这关于 [[Class]] :

The value of a [[Class]] internal property is used internally to distinguish different kinds of objects. Note that this specification does not provide any means for a program to access that value except through Object.prototype.toString (see 15.2.4.2).



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

所以,Object.prototype.toString 是它应该如何工作的。很明显,IE8 在这方面对于 localStorage 对象存在错误。我们无法知道 IE8 内部是否 toString() 没有使用 [[Class]][[Class]] 是否设置不正确。无论如何,IE8 中的 console.log() 似乎没有直接使用 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"
  • 关于javascript - 奇怪的 IE8 内部 [[ class ]] 属性行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9872067/

    有关javascript - 奇怪的 IE8 内部 [[ class ]] 属性行为的更多相关文章

    1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

      我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

    2. ruby - Ruby gsub 替换中的行为不一致? - 2

      两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

    3. ruby-on-rails - Ruby 中意外的大小写行为 - 2

      我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。

    4. ruby - 使对象的行为类似于 ruby​​ 中并行分配的数组 - 2

      假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje

    5. Ruby - 如何处理子类意外覆盖父类(super class)私有(private)字段的问题? - 2

      假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案

    6. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

      我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

    7. ruby - 无法理解 `puts{}.class` 和 `puts({}.class)` 之间的区别 - 2

      由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A

    8. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

      我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

    9. ruby - 是否可以从也在该模块中的类内部调用模块函数 - 2

      在这段Ruby代码中:ModuleMClassC当我尝试运行时出现“'M:Module'的未定义方法'helper'”错误c=M::C.new("world")c.work但直接从另一个类调用M::helper("world")工作正常。类不能调用在定义它们的同一模块中定义的模块函数吗?除了将类移出模块外,还有其他解决方法吗? 最佳答案 为了调用M::helper,你需要将它定义为defself.helper;结束为了进行比较,请查看以下修改后的代码段中的helper和helper2moduleMclassC

    10. ruby-on-rails - 浮点乘法的 Ruby 奇怪问题 - 2

      有没有人用ruby​​解决这个问题:假设我们有:a=8.1999999我们想将它四舍五入为2位小数,即8.20,然后乘以1,000,000得到8,200,000我们是这样做的;(a.round(2)*1000000).to_i但是我们得到的是8199999,为什么?奇怪的是,如果我们乘以1000、100000或10000000而不是1000000,我们会得到正确的结果。有人知道为什么吗?我们正在使用ruby​​1.9.2并尝试使用1.9.3。谢谢! 最佳答案 每当你在计算中得到时髦的数字时使用bigdecimalrequire'bi

    随机推荐