一句话闭包
- 闭包允许函数访问并操作函数外部的变量 --JS 忍者秘籍
- 闭包是指有权访问另外一个函数作用域中的变量的函数 -- 红宝书
- 闭包是指那些能够访问自由变量的函数,这里的自由变量是外部函数作用域中的变量 -- MDN
作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域主要两种:全局作用域和函数作用域。
其次:块级作用域
先看下面一段代码
// 全局作用域
var a = 1;
var f = 5;
function fu() {// 函数作用域
var a = 2;
var b = 3;
console.log(a, f); // 2, 5
}
fu();
console.log(a); // 1
// console.log(b); // b is not defined
if (true) {// 块级作用域
var c = 5;
let d = 6;
}
console.log(c); // 5
// console.log(d); // d is not defined
我们通过上面这段代码,发现Javascript语言的一些特殊之处:
- 函数内部可以直接读取全局变量
- 函数外部无法读取函数内的局部变量(b is not defined)
- 块级作用域内通过let、const创建的变量在外部无法读取, 详解(d is not defined)
作用链
当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域继续查找...一直找到全局作用域, 没找到则报错 XXX is not defined 。
闭包的形成
先看一下这个列子 列1:
var b =13
function foo(){
var b =14
return function fo(){
console.log(b)
}
}
foo()() // 14
我们可能会觉得很奇怪,上面的代码不是相当于下面这样子么? 列2:
var b =13
function fo(){
console.log(b)
}
fo() // 13
我们在看下这列子,发现与列2同理。 列3:
var b =13
function foo(){
return function fo(){
console.log(b)
}
}
foo()(); // 13
解析下以红宝书为列:闭包是指有权访问另外一个函数作用域中的变量的函数
画下重点:
- 访问另外一个函数作用域中的变量
- 是一个函数 从中可得列1是一个闭包
var b =13
function foo(){
var b =14
return function fo(){ // 一个函数
// 这里访问了另外一个函数作用域中的变量(访问了foo函数作用域的变量b)
console.log(b)
}
}
foo()() // 14
闭包的作用
实现模块化,保护私有变量不被外部侵扰。
常见方法使用自执行函数实现闭包模块化
// 自执行函数实现模块化
// 模块1
(function () {
var a = 1;
console.log(a); // 1
})();
// 模块2
(function () {
var a = 2;
console.log(a); // 2
})();
模块1、模块2的a变量互不侵扰。
闭包的问题
function father(){
var b =14
return function child(){
console.log(b)
}
}
foo()() // 14
解析下上面闭包代码, father 函数作用域隔绝了外部环境,所有变量引用都在函数内部完成,father 运行完成以后,内部的变量就应该被销毁,内存被回收。然而闭包导致了全局作用域始终存在一个 child 函数在引用着 father 内部的 b 变量,这就意味着 father 内部定义的 child 函数引用数始终为 1,垃圾运行机制就无法把它销毁。引擎无法判断你什么时候还会调用闭包函数,只能一直让这些数据占用着内存, 从而导致 内存泄露。
内存泄露 是指当一块内存不再被应用程序使用的时候,由于某种原因,这块内存没有返还给操作系统或者内存池的现象。内存泄漏可能会导致应用程序卡顿或者崩溃。
经典面试题
问:分析它实际运行的结果
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i),0);
}
// 3 3 3
问:改造它,输出0 1 2
1.es6
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i),0)
}
// 0 1 2
2.setTimeout第三个参数
for (var i = 0; i < 3; i++) {
setTimeout((t) => console.log(t),0,i)
}
// 0 1 2
3.闭包
for (var i = 0; i < 3; i++) {
(function () {console.log(i)})()
}
// 0 1 2