- 同步:从上到下按顺序执行,默认情况下, 编写的代码都是同步的
- 异步:随时可以执行,所有函数的执行都是异步的
异步典型:for循环中定义函数
// 只有i加到10才不满足条件, 所以循环执行完毕i=10
for(var i = 0; i < 10; i++){
// 所有函数的执行都是异步的
function test() {
// 当函数调用时循环已经执行完毕了, 所以拿到的是10,
//此处定义函数但并未执行,所以不会打印i
console.log(i);
}
}
// 只有for循环执行完毕, 才会执行调用test的代码
test(); //10
改进:如果想要使for循环中的i和函数保持同步
for(var i = 0; i < 10; i++){
// 想要test函数中输入的i和循环的i同步, 必须每次执行循环都调用一次
function test() {
// 每次执行循环都调用了一次函数, 所以i就是当前的值
console.log(i);
}
test();
}
优化:上述代码的另一种写法
for(var i = 0; i < 10; i++){
// 立即执行函数,定义后马上执行
(function(index) {
console.log(index);
})(i);
}
- 范例
// 1.拿到所有的按钮
var oBtns = document.querySelectorAll("button");
// 2.给所有按钮添加点击事件
// 默认情况下所有的代码都是同步的
for(var i = 0, len = oBtns.length; i < len; i++){
var oBtn = oBtns[i];
// 这里的立即执行函数是为了保证index的值和循环的值同步
// 这里的立即执行函数保存了将来事件异步函数要用到的index
(function (index) {
// 这里注册的事件是一个闭包, 闭包中用到了刚才同步的index,
// 所以每一个立即执行函数都不会被销毁,而是保留在内存中
// 所以将来执行闭包时可以拿到index(闭包的要义)
oBtn.onclick = function () {
// 监听点击事件的函数, 用到了其它函数中的变量
// 所以这里监听事件的函数, 是一个闭包
console.log(index);
}
})(i);
}
-
ES6解决了这个问题
上代码
window.onload=function (){
let aInput = document.querySelectorAll('input');
for(let i=0; i<aInput.length; i++){
aInput[i].onclick=function(){
console.log(i);
}
}
};
//当点击第二个按钮时会打印2
- 原因:因为ES6新增了块级作用域的概念
变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是对应的i值。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。