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

eto*_*leb 25 javascript internet-explorer scope browser-bugs

运行以下代码块时,typeof(hiya) = string在IE7/8输出时输出FF和Chrome 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>
Run Code Online (Sandbox Code Playgroud)

以下每个问题都会消失:

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

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

Jua*_*des 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>
Run Code Online (Sandbox Code Playgroud)

因此,如果你把所有东西都放在一个脚本标签中,这就是代码将会是什么(在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>
Run Code Online (Sandbox Code Playgroud)


Ray*_*nos 10

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

[编辑]

从规范.第38页:

对于代码中的每个VariableDeclaration或VariableDeclarationNoIn,创建变量对象的属性,其名称为VariableDeclaration或VariableDeclarationNoIn中的标识符,其值未定义且其属性由代码类型确定.如果已存在具有声明变量名称的变量对象的属性,则不会更改该属性的值及其属性.

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

[/编辑]

感谢@TimDown和@JuanMendes指出,将属性写入窗口对象是一个变量声明是一个问题.

问题:

变量声明被移动到块的顶部.即使代码已经死了.在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(...)
Run Code Online (Sandbox Code Playgroud)

在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(...)
Run Code Online (Sandbox Code Playgroud)

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