【技术杂货铺】JS中的闭包是个什么东西
我自己想要了解到闭包这个概念是因为在上一篇随笔,介绍防抖和节流的时候,有一段代码看得我有点犯迷糊,如下
function debounce(fn, delay) { var timer = null; return function() { if (timer) clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } }
按照我之前的了解,每次调用这个debounce防抖函数的时候,timer都要被重新赋值为null,这怎么起得到定时器的作用呢?后来了解到这是因为闭包的存在,所以timer这个变量在内存一直保存下来,而不是重新清空。好了,下面正式介绍闭包就能知道我在唠叨什么了。
要说清楚这个问题,我觉得要先从基础说起,并且清楚里面的一些细节,才能理解清楚。js的变量分为全局变量和局部变量。
全局变量:可以在任意位置访问的变量就是全局变量。例如下面的age。
var age = 10; function a(){ console.log(age); //输出为10 }
局部变量:函数中用var定义的变量,只能在函数中访问这个变量,函数外部访问不了。
function a(){ var age = 10; } a(); console.log(age); //Uncaught ReferenceError: age is not defined
OK,上面这些大家应该都是很熟悉的了,那么有两个注意点要注意,
1.在函数中如果不适用var定义变量那么js引擎会自动将该变量变为全局变量。
2.全局变量自创建起就一直保存在内存,直到你关闭页面,而局部变量是当其所在的函数运行完后就会销毁,假如再次调用该函数,会再次重新创建这个变量,即运行完就销毁,回到最初的状态。
再来说说函数,一个函数可以套很多别的函数,函数里面的子函数可以访问到它上级函数定义的变量,这个上级可以像冒泡一样,一直往上找,如果找到全局变量都找不到,那么就会报错。
闭包
下面要通过一个例子来引入闭包
function a(){ var count = 0; console.log(++count); } a(); //输出1 a(); //还是输出1
上面的代码输出了两个1,是因为上面的局部变量在函数执行完一次后就会销毁,不会保存在内存,再次调用时会重新创建那个变量。那么问题来了,我们怎样做才能确保第一次的创建的变量不会被销毁呢,就是说,如果让第二次调用该函数时,输出的是2,不是1呢?这就需要我们的闭包出来了。
Warning:JavaScript中有回收机制,函数没有被引用执行完以后这个函数的作用域就会被销毁,如果一个函数被其他变量引用(重要),这个函数的作用域将不会被销毁,(简单来说就是函数里面的变量会被保存下来,你可以理解成全局变量。)
好了,下面请出我们的闭包:
function a(){ var count = 0; function b(){ count ++; console.log(count); } return b; } var ab = a(); ab(); //输出1 ab(); //输出2
这里可以看到,里面的变量count的值并没有重新计算,因为函数a被外部的变量ab引用,所以变量count没有被回收。
如果某个函数被它的父函数之外的一个变量引用,就形成了一个闭包。
还有一种更为常用的闭包写法:
var ab = (function(){ var count = 0; function b(){ count++; console.log(count); } return b; })(); ab(); //1 ab(); //2 ab(); //3
执行过程分析:首先把一个自执行函数赋值给了ab,这个自执行函数运行完后ab的值就变为了
function b(){ count++; console.log(count); }
因为我们在上面的代码return回去了b,表示将b赋值给了ab,然后因为这个自执行函数被ab引用所以里面的count变量并没有因为这个自执行函数执行完而销毁,而是保存到了内存中,所以多次打印会累加。

浙公网安备 33010602011771号