闭包:有权访问另一个函数作用域中变量的函数。
闭包分析
function outer(p) {
var a = 1
return function inner() {
console.log('p:' + p, 'a:' + a)
}
}
var fn = outer(2)
fn()
- 开始执行程序,提升变量:
outer函数和fn变量,创建全局上下文对象 GO - 执行
outer函数,传入参数2,将其执行上下文压入call stack - 创建函数
outer变量对象 VO,VO包含arguments、参数、被提升的变量声明、this,并把 VO 放到作用域链 [[scope]] 的顶层 - 执行
a = 1,赋值变量a(变量a的查找遵循作用域链查找规则: VO -> GO 的顺序) - 返回函数
inner - 函数
outer执行完成后其执行上下文将从call Stack中弹出,本来其中创建的 VO 也应被随之销毁,但是其内部函数inner[[scope]] 引用了 VO,所以 VO 此时不会被销毁 - 将返回的函数
inner赋值给变量fn - 执行函数
fn,其[[scope]]如图:
闭包的应用
for循环变量引用
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
})
}
// 将打印 5 个 5
// 使用闭包修改后
for (var i = 0; i < 5; i++) {
(function(num) {
setTimeout(() => {
console.log(num)
})
})(i)
}
代码模块化 因为js并没有块级作用域(es2015之前),只有 全局作用域 和 函数作用域,可以利用函数闭包的特性隐藏某些变量,实现模块化
var counter = (function() {
var privateCount = 1
function changeCount(val) {
privateCount += val
}
return {
increase() {
changeCount(1)
},
decrease() {
changeCount(-1)
},
getCount() {
return privateCount
}
}
})()
缓存
var sum = (function() {
var cache = {}
return function (a, b) {
// 如果函数中是一些比较耗时的操作,且操作是幂等的,就可以进行缓存
if (cache[`${a}+${b}`] !== undefined) {
return cache[`${a}+${b}`]
}
const total = a + b
cache[`${a}+${b}`] = total
return total
}
})()
单例类
var User = (function () {
var instance = null;
return function inner(name) {
if (instance !== null) return instance;
this.name = name;
instance = this;
};
})();
闭包可能带来的问题
因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响, 其会根据闭包数量的多少而在内存中创建更多的变量对象, 最终可能会导致内存溢出 等情况!
不合适的闭包使用可能带来内存泄漏,但是这不是闭包的问题。