闭包与循环索引同步
闭包 (新奇的解释)
- 什么是闭包(closure)?
- 闭包是一种特殊的函数。
- 如何生成一个闭包?
- 当一个内部函数引用了外部函数的数据(变量/函数)时, 那么内部的函数就是闭包
- 所以只要满足**"是函数嵌套"、"内部函数引用外部函数数据"**
- 闭包特点:
- 只要闭包还在使用外部函数的数据, 那么外部的数据就一直不会被释放
- 也就是说可以延长外部函数数据的生命周期
- 闭包注意点:
- 当后续不需要使用闭包时候, 一定要手动将闭包设置为null, 否则会出现内存泄漏
// 常规函数
function test() {
var i = 666; // 局部变量
} // 只要代码执行到了大括号结束, i这个变量就会自动释放
console.log(i); // i is not defined
function test() {
var i = 666;
// 由于demo函数满足闭包的两个条件, 所以demo函数就是闭包
return function demo() {
console.log(i);
}
}
let fn = test();
fn(); // 666
循环索引同步
- 要拎清: 函数定义和函数执行
最基本的认识
- JavaScript 是单线程的
var
- 默认情况下通过 var 定义的变量, 只要不是定义在函数中都是==全局变量==
for 循环用 var, for 完调函数
for 完了才执行到 test, 此时全局变量 i 已经是 3 了, 我们称这种情况为循环索引==不同步==
for(var i = 0; i < 3; i++){ // 0 1 2 3
function test() {
console.log(i); // 3
}
}
test();
for 循环用 var, for 里面调函数
for (var i = 0; i < 3; i++) {
function test() {
console.log(i); // 0 // 1 // 2
}
test();
}
for 循环里面用立即执行函数
for(var i = 0; i < 3; i++){
(function test() {
console.log(i); // 0 // 1 // 2
})();
}
闭包结合 var 循环索引同步
for 内调函数, 同义变体
for(var i = 0; i < 3; i++){
// function test(index) {
// console.log(index); // 0 // 1 // 2
// }
// test(i);
(function test(index) {
console.log(index); // 0 // 1 // 2
})(i);
}
这样==就能==点第几个按钮就在控制台输出几 (从零开始)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>22-JavaScript-循环索引同步练习</title>
</head>
<body>
<button>我是按钮1</button>
<button>我是按钮2</button>
<button>我是按钮3</button>
<script>
// 不能同步, 原理与 for 完调函数相同, for 才有 "每个 button 上都绑定了 onclick 事件", 此时 i 已经是 3
/*
let oBtns = document.querySelectorAll("button");
for(var i = 0; i < oBtns.length; i++){
let btn = oBtns[i];
btn.onclick = function () {
console.log(i); // 3
}
}
*/
// 可以同步
/*
for(var i = 0; i < 3; i++){
(function test(index) { // var index = i;
console.log(index); // 0 1 2
})(i);
}
*/
let oBtns = document.querySelectorAll("button");
for(var i = 0; i < oBtns.length; i++) {
let btn = oBtns[i];
(function test(index) { // var index = i;
// console.log(index); // 0 1 2
// 注意点: onclick对应的方法由于满足了闭包的条件, 所以onclick对应的方法也是一个闭包
btn.onclick = function () {
console.log(index);
}
})(i);
}
</script>
</body>
</html>
==这样又不行==, 个中差别只在于是否立即执行, 给参数赋值 (上面那种) 是 js 立即就干了的, 而触发点击事件却不是. 妈欸好鬼酷哦
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>22-JavaScript-循环索引同步练习</title>
</head>
<body>
<button>我是按钮1</button>
<button>我是按钮2</button>
<button>我是按钮3</button>
<script>
// 不能同步
let oBtns = document.querySelectorAll("button");
for (var i = 0; i < oBtns.length; i++) {
let btn = oBtns[i];
(function test() {
btn.onclick = function () {
console.log(i);
};
})();
}
</script>
</body>
</html>
let 天然可以循环索引同步
- 在ES6中如果在循环中通过 let 定义的变量, 那么这个变量是一个==局部变量==
- 由于 i 是局部变量, 所以每次执行完循环体都会重新定义一个 i 变量, 且这个 i 并不对之前的 i 进行覆盖, 相互之间并不相干
- 在 ES6 中由于 {} 是块级作用域, 所以只要在块级作用域中定义了一个函数 并且这个函数中用到了块级作用域中的数据, 那么这个函数就是闭包
var list = [];
// 这里的i是全局变量
for(var i = 0; i < 3; i++){
var fn = function test() {
console.log(i); // 3 // 3 // 3
}
list.push(fn);
}
console.log(i); // 3
list[0]();
list[1]();
list[2]();
let list = [];
for (let i = 0; i < 3; i++) {
let fn = function test() {
console.log(i); // 0 // 1 // 2
};
list.push(fn);
}
// console.log(i); // Uncaught ReferenceError: list is not defined
list[0]();
list[1]();
list[2]();
var 与 let 搞笑区别
- 在ES6中由于 {} 是块级作用域, 所以只要在块级作用域中定义了一个函数
- 并且这个函数中用到了块级作用域中的数据, 这个函数就是闭包
for(var i = 0; i < 3; i++){ // 0 1 2 3
function test() {
console.log(i); // 3
}
}
test();
for(let i = 0; i < 3; i++){
function test() {
console.log(i); // 2
}
}
test();
闭包与 let, 优雅现代
- {} 的块级只能通过 let 来达成, 用了 let 后 {} 包着的函数 prone to 形成闭包
- 在ES6中
- for循环中通过let定义的变量是一个局部变量
- 2.for循环中通过let定义的变量每次执行循环体都会重新定义一个新的属于自己的变量
- for循环中如果定义了函数, 这个函数用到了通过let定义的变量, 那么这个函数是一个闭包
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>22-JavaScript-循环索引同步练习</title>
</head>
<body>
<button>我是按钮1</button>
<button>我是按钮2</button>
<button>我是按钮3</button>
<script>
let oBtns = document.querySelectorAll("button");
for(let i = 0; i < oBtns.length; i++){
let btn = oBtns[i];
btn.onclick = function () {
console.log(i);
}
}
</script>
</body>
</html>