25

运行以下代码块时,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 = 1var hiya2 = 1.
  • 重命名var hiya = 1window.hiya = 1.
  • 重命名var hiya = 1hiya = 1.

怎么了?IE 中是否存在范围界定错误?

4

3 回答 3

27

IE 很笨,在某些情况下它无法识别window.varName并访问相同的变量。var varName

当遇到新的 script 标签时,它首先初始化所有用 var 声明的变量。它不运行 var 语句(将其初始化为“hiya”的部分)。它只是将其初始化为未定义。如果它以前用 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>
于 2011-01-05T18:24:34.867 回答
10

核心问题可以在这里看到http://jsfiddle.net/Raynos/UxrVQ/我还没有弄清楚为什么 IE 会在没有检查的情况下覆盖 window.hiya。

[编辑]

从规范。第 38 页:

对于代码中的每个VariableDeclaration或VariableDeclarationNoIn,创建一个变量对象的属性,其名称为VariableDeclaration或VariableDeclarationNoIn中的Identifier,其值未定义,其属性由代码类型决定。如果变量对象的属性已经具有声明变量的名称,则该属性的值及其属性不会改变。

一种可能的解释是,在全局范围内,IE在声明变量时区分了windowobject 和for 全局范围。variable object或者,window直接在对象上设置属性可能不会在对象上设置相同的属性variable。如果你能找到一个正式的 JScript 规范或有 IE 的源代码,那么我们就能准确地找出其中的怪癖。

[/编辑]

感谢@TimDown 和@JuanMendes 指出将属性写入窗口对象是否是变量声明的问题。

问题:

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

解释:

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

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

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

它实际上在做什么

在火狐中:

// 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(...)

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

于 2011-01-05T17:24:46.470 回答
3

你遇到的原因是:

  1. var作为一个声明
  2. JS 中没有块作用域
  3. 在代码运行之前执行语句

所以发生的情况是 JavaScript 将在执行var任何其他操作之前执行该语句,但它不会评估赋值表达式,因此hiya将默认为undefined.

正如 Raynos 已经说过的,IE 将自行执行每个脚本,因此上述行为将导致hiya未定义。

于 2011-01-05T17:35:15.357 回答