实战例题1:求下面代码的输出
const foo = (function() {
var v = 0
return () => {
return v++
}
}())
for (let i = 0; i < 10; i++) {
foo()
}
console.log(foo())
答案:10
解析:
foo 是一个立即执行函数,我们尝试打印foo:
const foo = (function() {
var v = 0
return () => {
return v++
}
}())
console.log(foo)
输出:
() => {
return v++
}
在循环执行时,执行 foo() , 这样引用自由变量10次, v 自增 10次,最后执行 foo 时,得到10. (自由变量是指没有在相关函数作用域中声明,但是使用了变量。)
实战例题2: 求下面代码的输出
const foo = () => {
var arr = []
var i
for (i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
return arr[0]
}
foo()()
答案:10,这时自由变量为 i,分析类似例题 1:foo() 执行返回的是 arr[0], arr[0] 此时是函数:
function () {
console.log(i)
}
变量 i 值为 10.

这个我到浏览器中输出然后理解了下,发现大致思路是对的。
实战例题3:求下面代码的输出
var fn = null
const foo = () => {
var a = 2
function innerFoo() {
console.log(a)
}
fn = innerFoo
}
const bar = () => {
fn()
}
foo()
bar()
输出结果是2, 首先这个函数 foo 先行执行, 内部函数innerFoo 赋值给了全局变量 fn,由于innerFoo 调用了a,所以变量 a 得以保存, bar() 函数开始调用, fn() 调用,接着输出就是2.不知道我解释清楚了没有。
给出一波官方大佬的解释。
正常来讲,根据调用栈的知识,foo 函数执行完毕之后,其执行环境生命周期会结束,所占内存被垃圾收集器释放,上下文消失。但是通过 innerFoo 函数赋值给 fn,fn 是全局变量,这就导致了 foo 的变量对象 a 也被保留了下来。所以函数 fn 在函数 bar 内部执行时,依然可以访问这个被保留下来的变量对象,输出结果为 2。
例题4:求下面代码的输出
我们将上面的例子稍作修改:
var fn = null
const foo = () => {
var a = 2
function innerFoo() {
console.log(c)
console.log(a)
}
fn = innerFoo
}
const bar = () => {
var c = 100
fn()
}
foo()
bar()
执行结果是:报错。
例题 4 分析
在 bar 中 执行 fn()时,fn() 已经被复制为 innerFoo, 变量 c 并不在其作用域链上,c 只是 bar 函数的内部变量。因此报错 ReferenceError: c is not defined。
图示分析:

思考例题 5:如何利用闭包实现单例模式
单例模式,是一种常用的软件设计模式。GoF 在 《设计模式:可复用面向对象软件的基础》一书中给出了如下定义:
Ensure a class only has one instance, and provide a global point of access to it.
保证一个类只有一个实例,并提供一个访问它的全局访问点。
使用闭包我们可以保持对实例的引用,不被垃圾回收机制回收,因此:
function Person() {
this.name = 'lucas'
}
const getSingleInstance = (function(){
var singleInstance
return function() {
if (singleInstance) {
return singleInstance
}
return singleInstance = new Person()
}
})()
const instance1 = new getSingleInstance()
const instance2 = new getSingleInstance()
单例模式的引用,这段代码貌似好强的样子。
事实上,我们有 instance1 === instance2。 因为借助闭包变量 singleInstance, instance1 和 instance2 是同一引用的(singleInstance),这正式单例模式的体现。
最后放上大佬的总结
本课我们通过介绍理论知识加例题实战的方式梳理了 JavaScript 中闭包、内存、执行上下文、作用域、作用域链等概念。
这些内容说基础,确实不算很难;说复杂,它绝对又能衍生出很多知识点。这些知识点不是 JavaScript 所特有的,但是在前端开发中又极具自身语言风格。它绝不只是纯理论概念,只有解决真实的开发问题才有实际意义。
一个合格的高级前端工程师需要做的并不是如数家珍地背诵“闭包和 GC 原理”,而是根据面临的场景,凭借扎实的基础,能够通过查阅资料,提升应用性能,分析内存事故和突破瓶颈。
文章采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接。