为什么IE核对window.ABC变量?

时间:2011-01-05 17:00:46

标签: javascript internet-explorer scope browser-bugs

运行以下代码块时,FF和Chrome输出typeof(hiya) = string,而IE7 / 8输出typeof(hiya) = undefined

<html>
    <body>
        <script type="text/javascript">
            window.hiya = 'hiya';
        </script>
        <script type="text/javascript">
            if( false ) {
                var hiya = 1;
            }
            document.write( "typeof(hiya) = "+ typeof(hiya) );
        </script>
    </body>
</html>

以下各项都会解决问题:

  • 将所有内容合并为一个<script>块。
  • 删除if块。
  • var hiya = 1重命名为var hiya2 = 1
  • var hiya = 1重命名为window.hiya = 1
  • var hiya = 1重命名为hiya = 1

发生了什么事? IE中是否存在范围错误?

3 个答案:

答案 0 :(得分:26)

IE很愚蠢,在某些情况下,它无法识别window.varNamevar varName访问同一个变量。

当遇到新的脚本标记时,它首先初始化用var声明的所有变量。它不运行var语句(将其初始化为“hiya”的部分)。它只是初始化为undefined。如果先前使用var声明它,它将不会这样做。

如果您的代码位于单个脚本标记中,则不会发生此错误。另外,如果hiya的第一个声明是用var完成的,那么这个错误也不会发生。

具体来说,在第二个脚本标记中,IE首先查找var语句,它会找到var var hiya = 1;然后它说,hiya以前没有用var语句初始化(IE是愚蠢的,其他浏览器认识到window.hiya做同样的事情)并初始化hiya,在执行任何代码之前覆盖window.hiya。

可能的解决方案:

  • 将代码保存在同一个脚本标记
  • 不要使用window.hiYa
  • 初始化变量
  • 如果您不控制其中一个脚本,请确保首先使用var的脚本

最后请注意澄清JS解析器对您的代码所做的事情。当JS解析器看到您的代码时,它会将其转换为以下内容:

<html>
    <body>
        <script type="text/javascript">
            window.hiya = 'hiya';
        </script>
        <script type="text/javascript">
            // IE is dumb, it doesn't recognize that hiya is already 
            // defined as window.hiya, so it's initialized to undefined here
            var hiya;
            if( false ) {
                hiya = 1;
            }
            document.write( "typeof(hiya) = "+ typeof(hiya) );
        </script>
    </body>
</html>

因此,如果将所有内容放入一个脚本标记中,这就是代码所在的内容(在JS引擎将var语句移到顶部之后),因此您可以看到IE无法将其搞砸,因为你的window.hiya作业将在移动到顶部的var之后。

<html>
    <body>
        <script type="text/javascript">
            var hiya;
            window.hiya = 'hiya';
            if( false ) {
                hiya = 1;
            }
            document.write( "typeof(hiya) = "+ typeof(hiya) );
        </script>
    </body>
</html>

答案 1 :(得分:10)

核心问题可以在这里看到http://jsfiddle.net/Raynos/UxrVQ/我还没有找到为什么IE覆盖window.hiya而不检查。

<强> [编辑]

从规范。第38页:

  

对于每个VariableDeclaration或   代码中的VariableDeclarationNoIn,   创建变量的属性   名称为标识符的对象   VariableDeclaration或   VariableDeclarationNoIn,其值   是未定义的,其属性是   由代码类型决定。如果   已经有了财产   名称为a的变量对象   声明变量,值的   属性及其属性不是   改变。

可能的解释是,在全局范围内,IE在声明变量时区分全局范围的window对象和variable object。或者,直接在window对象上设置属性可能不会在variable对象上设置相同的属性。如果您可以找到正式的JScript规范或者有IE的来源,那么我们就可以确切地知道怪癖是什么。

<强> [/编辑]

感谢@TimDown&amp; @JuanMendes指出,向window对象写一个属性是一个变量声明是一个问题。

问题:

变量声明被移动到块的顶部。即使代码已经死了。在IE中出于某种原因,它会将hiya声明为局部变量,即使它使用存储在窗口中的同名属性进行分类。

<强>解释

发生的事情是你声明一个名为hiya的变量。 var语句自动被删除到块的顶部。 if语句不是块,函数是。因此,如果代码永远不会在块中运行,则变量仍会被声明。

在Firefox中,它会识别出window.hiya是hiya的声明。

在IE中,第二个脚本中的声明会覆盖它

它正在做什么

在firefox中:

// script block 1
var hiya; // window.hiya counts as a declaration
window.hiya = "hiya"; // set

// script block 2
if (false) hiya = 1;
document.write(...)

在IE中:

// script block 1
window.hiya = "hiya";

// script block 2
var hiya; // redeclared here because window.hiya "isn't" a declaration
if (false) hiya = 1; 
document.write(...)

解决方案只是命名空间。您在两个地方使用相同的名称,并以两个不同的名称访问它。使用不同的名称或使用闭包来给出局部范围。

答案 2 :(得分:3)

您遇到的原因是:

  1. var是一份声明
  2. JS
  3. 中没有块范围
  4. 在代码运行之前执行语句
  5. 所以会发生的事情是JavaScript会先于var语句执行,但它不会评估赋值表达式,因此hiya将默认为undefined的值。

    由于Raynos已经声明IE将自己执行每个脚本,因此上述行为将导致hiya未定义。