探索js闭包的由来与应用场景(js闭包的定义和用途)
一句话闭包
闭包允许函数访问并操作函数外部的变量 --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
作者:逆风起飞
链接:https://juejin.cn/post/7047439466184572958