打印

BX9056: 各浏览器下 window.eval 方法的执行上下文存在差异

作者:钱宝坤

标准参考

eval 方法,可以执行一段脚本,该脚本的上下文是当前代码的上下文执行的,如果没有指定上下文,该脚本将在全局上下文中执行。

该方法属于 native code 范畴 ,由全局对象 Global 提供支持。

关于 eval 的详细描述可以参见:ECMA 262 第五版

问题描述

eval 方法在各浏览器都按照规范定义,根据当前代码上下文执行 eval 方法中的脚本,如果没有指定上下文,该脚本将在全局上下文中执行。
window.eval 方法,在 IE6 IE7 IE8 中依然在脚本所在上下文中执行,而 Firefox Chrome Safari Opera 中则将该脚本会在全局上下文中执行。

造成的影响

window.eval 方法执行一段代码时,若在指定上下文中定义了变量, IE6 IE7 IE8 在全局上下文中无法访问该变量,导致代码报错。

受影响的浏览器

所有浏览器  

问题分析

分析以下代码:

<script>
  var cec = 'calling execution context: ',
    gec = 'glocal execution context: ',
    ok = '<span class="ok">OK</span><br />',
    fail = '<span class="fail">Fail</span><br />';
</script>
<h1><strong>&quot;eval&quot;</strong> in <em>global</em> execution context:</h1>
<script>
  eval('var a = 1;');
  void function () {
    try {
      if (a == 1) {
        document.write(cec + ok);
      }
    } catch(ex) {
      document.write(cec + fail);
    }
  }();
  try {
    if (a == 1) {
      document.write(gec + ok);
    }
  } catch(ex) {
    document.write(gec + fail);
  }
</script>
<br />
<h1><strong>&quot;eval&quot;</strong> in <em>calling</em> execution context:</h1>
<script>
  void function () {
    eval('var b = 1;');
    try {
      if (b == 1) {
        document.write(cec + ok);
      }
    } catch(ex) {
      document.write(cec + fail);
    }
  }();
  try {
    if (b == 1) {
      document.write(gec + ok);
    }
  } catch(ex) {
    document.write(gec + fail);
  }
</script>
<br />
<h1><strong>&quot;window.eval&quot;</strong> in <em>global</em> execution context:</h1>
<script>
  window.eval('var c = 1;');
  void function () {
    try {
      if (c == 1) {
        document.write(cec + ok);
      }
    } catch(ex) {
      document.write(cec + fail);
    }
  }();
  try {
    if (c == 1) {
      document.write(gec + ok);
    }
  } catch(ex) {
    document.write(gec + fail);
  }
</script>
<br />
<h1><strong>&quot;window.eval&quot;</strong> in <em>calling</em> execution context:</h1>
<script>
  void function () {
    window.eval('var d = 1;');
    try {
      if (d == 1) {
        document.write(cec + ok);
      }
    } catch(ex) {
      document.write(cec + fail);
    }
  }();
  try {
    if (d == 1) {
      document.write(gec + ok);
    }
  } catch(ex) {
    document.write(gec + fail);
  }
</script>

上述代码分别检测了当使用 eval 及 window.eval 方法分别在全局作用域及局部作用域下执行声明并初始化变量的脚本代码,然后又分别在这两种作用域下尝试使用声明的变量,检查是否变量是否可用以及是否为初始化的值。

各浏览器执行结果如下:

IE6 IE7 IE8 Firefox Chrome Safari Opera

可见,对于直接调用的 eval 方法在各浏览器都按照规范定义,根据当前代码上下文执行 eval 方法中的脚本,如果没有指定上下文,该脚本将在全局上下文中执行。

当使用 window.eval 方法时,

  • IE6 IE7 IE8 中,window.eval 方法等同于直接调用的 eval 方法,会根据当前代码上下文执行 window.eval 方法中的脚本,此时浏览器认为没有指定上下文;
  • Firefox Chrome Safari Opera 中,浏览器认为 window.eval 方法确定了全局作用域,则其内脚本代码将在全局上下文中执行。

由于 window 对象属于宿主环境 ( host environment ),由 window 对象提供的 eval 方法不是规范中提到的与宿主环境无关的 Global Natvie Code。

此问题中, IE6 IE7 IE8 中处理情况可以认为是将 Gloabl 全局对象与 Host 宿主对象统一处理了,window.eval 即是 Global.eval,他会根据当前代码上下文确定执行内容的作用域位置。

而其他浏览器则与规范描述相吻合,他们都认为 window 中的 eval 是 Host 对象提供的,其作用域应与 Host 环境一致,而不用考虑代码在全局上下文中的位置。另一个直接调用的 eval 方法才是全局对象的,应该根据规范描述依赖执行上下文决定其中代码的作用域。

解决方案

IE 中的 window.eval 方法不会产生全局上下文,对内部需要指定全局作用域环境的属性(变量),请在前面逐个加入window 关键字,以便确定该属性(变量)执行时处于指定的 window 全局作用域环境。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.10
Chrome 7.0.552.0 dev
Safari 5.0.2
Opera 10.63
测试页面: eval.html
本文更新时间: 2010-10-26

关键字

eval window.eval context 作用域