如果Javascript中的"with"语句创建了一个新范围,为什么每次关闭时此关闭都不包含新范围中的新"x"?

nop*_*ole 3 javascript closures scope

如果withJavascript中的语句创建了一个新范围,那么不应该点击链接显示x不同范围内的不同内容吗?它没有.

<a href="#" id="link1">ha link 1</a>
<a href="#" id="link2">ha link 2</a>
<a href="#" id="link3">ha link 3</a>
<a href="#" id="link4">ha link 4</a>
<a href="#" id="link5">ha link 5</a>


<script type="text/javascript">

    for (i = 1; i <= 5; i++) {

        with({foo:"bar"}) {
            var x = i;
            document.getElementById('link' + i).onclick = function() { alert(x); return false; }
        }

    }

</script>
Run Code Online (Sandbox Code Playgroud)

CMS*_*CMS 27

with语句不会创建一个全新的词法范围,它只是在范围链的前面引入一个对象,例如,如果捕获i变量,它将起作用:

for (var i = 1; i <= 5; i++) {
  with({x:i}) {
    document.getElementById('link' + i).onclick = function() {
      alert(x);
      return false;
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

让我试着用另一个例子更好地解释它:

var x = 10, y = 10;   // Step 1

with ({x: 20}) {      // Step 2

  var x = 30, y = 30; // Step 3

  alert(x); // 30
  alert(y); // 30
}

alert(x); // 10
alert(y); // 30
Run Code Online (Sandbox Code Playgroud)

在步骤1中,声明了xy变量,它们是作用域链中第一个对象(全局对象)的一部分.

在第2步中,语句{x:20}将一个新的object()引入作用域链with,现在作用域链如下所示:

   ________              ________
  | x = 10 | <--------- | x = 20 |
  | y = 10 |             ¯¯¯¯¯¯¯¯¯
   ¯¯¯¯¯¯¯¯ 

在步骤3中,var执行另一个语句,但它没有任何效果,因为正如我之前所说,只有函数创建一个完整的词法范围.

var语句没有任何效果,但是赋值具有,因此当x解析变量时,会在作用域链上的第一个对象上找到,即我们引入的对象with.

y标识符也解决了,但它不是链的第一个对象上找到,所以继续查找了,发现它的最后一个对象上,在分配后,作用域链是这样的:

   ________              ________
  | x = 10 | <--------- | x = 30 |
  | y = 30 |             ¯¯¯¯¯¯¯¯¯
   ¯¯¯¯¯¯¯¯ 

with语句结束,范围链终于恢复:

   ________ 
  | x = 10 |
  | y = 30 |
   ¯¯¯¯¯¯¯¯ 

编辑: 让我扩展一下,谈谈功能.

创建函数其当前父范围约束,例如:

var fn;
// augment scope chain
with ({foo: "bar"}) {
  fn = function () { // create function
    return foo;
  };
}??
// restored scope chain
fn(); // "bar", foo is still accessible inside fn
Run Code Online (Sandbox Code Playgroud)

在执行函数时,会创建一个新的词法范围并将其添加到范围链中.

基本上所有函数参数的标识符(名称),声明的变量var和用function语句声明的函数都被绑定为在幕后创建的新对象的属性,就在函数本身执行之前(当控件进入这个新的执行上下文时).

此对象无法通过代码访问,称为变量对象,例如:

var x = 10, y = 10;   // Step 1

(function () {        // Step 2
  var x, y; 

  x = 30;             // Step 4
  y = 30; 

  alert(x); // 30
  alert(y); // 30
})();                 // Step 3

alert(x); // 10       // Step 5
alert(y); // 10
Run Code Online (Sandbox Code Playgroud)

在第1步中,再次在我的第一个示例中,声明了xy变量,它们是作用域链中第一个对象(全局对象)的一部分.

在步骤2中,创建一个新的函数对象,此时将父作用域存储到该函数的[[Scope]]中,包含now xy.

在步骤3中,调用该函数,启动变量实例化过程,该过程在作用域链中创建一个新对象,包含在此新函数内声明的本地作用域xy变量,此时作用域链如下所示:

  parent scope           Variable Object
   ________              _______________
  | x = 10 | <--------- | x = undefined |
  | y = 10 |            | y = undefined | 
   ¯¯¯¯¯¯¯¯              ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 

然后在步骤4中,分配xy完成,但由于新的词法范围已创建,因此它不会影响外部值.

  parent scope           Variable Object
   ________              ________
  | x = 10 | <--------- | x = 30 |
  | y = 10 |            | y = 30 | 
   ¯¯¯¯¯¯¯¯              ¯¯¯¯¯¯¯¯ 

最后,在步骤5中,函数结束,范围链恢复到其原始状态.

   ________ 
  | x = 10 |
  | y = 10 |
   ¯¯¯¯¯¯¯¯ 

推荐讲座: