最近遇到一个有趣的面试题,需要实现一个函数,让函数第一次调用的时候打印1,第二次调用打印2,依次类推,第n次调用打印n。可以把题目理解为每次调用,显示函数的调用次数。
这个题的核心是使用闭包。首先按常规思路分析一下,如果要显示函数的调用次数,就需要一个常数去记录,每一次调用的时候让该常数+1即可。
let count = 0;
function allCount(){
console.log(++count);
}
但此种方法显然不符合面试官的意思,只用一个函数去解决。所以问题就转化为如何在函数内部使用一个函数,并且保存一个函数的值不被GC回收(也就是实现缓存)。闭包就能解决这个问题。
闭包的概念:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。通俗的一种闭包形式就是,一个函数内部有一个嵌套函数,并且内部的嵌套函数调用了其上层函数的变量,就形成了闭包。此时内部的变量处于调用状态,所以是不会被GC回收掉,那么就实现了我们需要的结果。
function allCount(){
let count = 0;
return function(){
console.log(++count);
}
}
类似的题目还有,实现一个函数,能够交替输出0 1同样也是使用闭包
function print01(){
let count = 0;
return function(){
if(count === 0){
console.log(++count)
}
else if(count === 1){
console.log(--count)
}
}
}
let test = print01();
setInterval(() => {
test()
},1000);
闭包是一种很好的机制,可以实现函数的缓存,像React Hooks的一些钩子函数实际上就是借助了闭包的特性,理解闭包可以和v8的垃圾回收机制一起结合理解,这样能够更深刻的理解闭包。