8

混乱作用域、闭包问题

 3 years ago
source link: http://misaka.im/index.php/archives/45/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

混乱作用域、闭包问题

2019.01.01默认分类 0 评
<button>A</button>
<button>B</button>
<button>C</button>
<button>D</button>
<p id="output"></p>
var buttons = document.querySelectorAll('button');
var output  = document.querySelector('#output');

for (var i = 0; i < buttons.length; ++i) {
    buttons[i].addEventListener('click', function (e) {
        output.innerText = buttons[i].innerText;
    }, false)
}
JavaScript

看似会正常运行的代码,居然在点击后出乎意料地报错了

Uncaught TypeError: Cannot read property 'innerText' of undefined
  • Click 事件触发时 i 变量已是 4
  • 循环时 i 变量存在于全局作用域
  • 运行时的监听器读取循环结束后的变量 i (4)

let 关键字

对于原来的代码,只需要改动一个地方即可,将对计数器 i 的定义从 var 改成 let,便可以在循环体内形成块级作用域,让每一次循环的执行都能保留当前计数器的数值和引用。—— 《实战 ES2015》

for (let i = 0; i < buttons.length; ++i) {
    buttons[i].addEventListener('click', function (e) {
        output.innerText = buttons[i].innerText;
    }, false)
}
JavaScript

使用立即执行函数包裹,解决闭包问题。

for (var i = 0; i < buttons.length; i++) {
    (function (index) {
        buttons[i].addEventListener('click', function (e) {
            output.innerText = buttons[index].innerText;
        }, false)
    })(i);
}
JavaScript

曲线救国

既然 i 变量有问题,为何不使用回调函数中的参数 e ,这样就可以读取按钮中文本了。

for (var i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener('click', function (e) {
        output.innerText = e.target.innerText;
    }, false)
}
JavaScript

错误的栗子

以上的代码,尝试用点击事件来说明作用域问题,使人处于一个可运行,又可能会出问题的迷茫中。

使用另外一个例子来说明

for(var i = 1; i <= 5; i++) {
   setTimeout(function() {
       console.log('Value of i : ' + i); 
   },100);
} 
JavaScript
Value of i : 6
Value of i : 6
Value of i : 6
Value of i : 6
Value of i : 6

问题便显而易见,使用上面的 let 关键字 或 IIFE 即可解决这个问题。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK