这两个for有什么区别呢

这两个for有什么区别呢
0
<div>
    <button class="btn">btn0</button>
    <button class="btn">btn1</button>
    <button class="btn">btn2</button>
    <button class="btn">btn3</button>
</div>
var btns = document.getElementsByClassName('btn');

for (var i = 0; i < btns.length; i++) {
     btns[i].onclick = function () {
          console.log(i);
     };
}

for (var i = 0; i < btns.length; i++) {
     btns[i].onclick = (function (j) {
          return function(){
              console.log(j);
          }
     })(i)
}

看起来都像是一样的

这里涉及到两个概念:闭包、函数输入参数的“按值传递”与“按引用传递”

以你的第一个 for 循环为例子,那里面的函数是一个“闭包”,因为它 可以直接访问不属于全局变量、也不属于其输入参数、还不属于函数体内部定义的局部变量 (变量 i)。在函数外面修改变量 i,函数里面也会读取到修改后的值;同样在函数里修改变量 i,函数外面也会读到修改后的值。

用第一个 for 循环的话,这些按钮绑定的 onclick 处理函数中读取的变量 i 与函数外面 for 语句中的变量 i 是同一个变量,因此你点击按钮触发执行函数时,函数里读取到的变量 i 的值已经在函数外的 for 循环中被修改成了 4 。所以你点击这几个按钮都会输出 4 。

第二个 for 循环里利用了 JavaScript 中函数的输入参数都是“按值传递”的特性。下面用一个代码示例来说明“按值传递”的参数传递方式:

function f(j) {
    j = 2;
}
var i = 1;
f(i);
console.log(i); // 输出 1

因为在执行函数时,函数里访问到的参数变量 j 与变量 i 并不是同一个变量,而是变量 i 的一个拷贝,因此在函数里修改 j 的值并不会影响 i 的值;反过来,在调用函数之后,修改函数外面的变量 i 也不会再影响到函数内参数变量 j 的值。

在第二个 for 循环中,在调用外层的函数时,函数中的参数变量 j 就是变量 i 的一个拷贝。例如在第一次循环(i = 0)时调用外层函数,参数变量 j 是变量 i 的一个拷贝(也就是 0),因此返回的内部函数能读到的 j 的值就是 0。然后在 for 语句中把变量 i 修改成 1,但这时不会影响第一次循环返回的函数能读取到的参数 j 的值了。在第二次循环(i = 1)调用执行外层函数时,参数变量 j 的值变成此时的变量 i 的拷贝,也就是 1 。要注意的是,这次调用时的对参数变量 j 的修改也不会影响第一次循环中的参数变量 j ,因为它们不在同一个执行上下文(context)中。因此,4 个按钮的 onclick 绑定的是分别能读取 4 个不同的参数 j 的值的 4 个函数,所以点击这 4 个按钮会分别输出 0、1、2、3 。

3赞

假设 JavaScript 的函数的输入参数是“按引用传递”的话,结果就会不一样了:

function f(j) {
    j = 2;
}
var i = 1;
f(i);
console.log(i); // 输出 2

因为如果输入参数是“按引用传递”的话,函数内读取到的参数变量 j 就是指向变量 i 的一个引用,你可以把参数变量 j 与变量 i 看作是同一个变量。

假设 JavaScript 的函数的输入参数都是“按引用传递”的话,你的代码中的两个 for 循环的效果就会变成一样的了,点击按钮都是输出 4 。

2赞