目录
1.闭包是什么?
2.闭包的优缺点
优点
缺点
3.闭包的演示
示例1
示例2
4.详细的闭包示例
示例1
5.闭包的原理
6.闭包的使用场景
js中的使用场景
闭包是指一个函数有权访问另一个函数作用域中变量的函数就叫做闭包
(1)闭包可以避免全局变量污染。
(2)闭包可以延长变量的生命周期。
(3)闭包中的变量不会被垃圾回收或者机制回收。
(1)由于变量长期都贮存在内存中,导致内存消耗大。因此不能滥用闭包,否则会造成网页的性能问题。
(2)在低版本IE浏览器中可能导致内存泄露。解决方法是在退出函数之前,将不使用的局部变量全部删除。
//函数局部作用域 function fn() { //1.在函数内部定义一个变量 var count = 10 //2.return返回这个f1()函数 return function fn1() { //3.变量减减 count-- console.log(count) } } //4.定义一个变量来接这个fn()函数 var ff = fn() //5.执行ff函数,此时这个ff函数代表的就是fn1()函数 ff()
在这段代码中,我们正在调用 `fn` 函数,fn函数返回其内部函数 `fn1`,并保存了count这个变量。当我们调用 ff()这个函数时),ff()函数被打印到控制台上。(实际上ff函数是在引用 fn函数里面的fn1函数里面的变量) 但是我们并没有在 `fn`1函数中声明 count这个 变量,因此即使外层函数返回后,该函数也可以以某种方式访问其外部函数fn的变量 `count`。因此,fn1 函数实际上产生了一个闭包。
function fn(){ var num=0; return function(){ return num++ } } var nums=fn() console.log(nums()) //0 console.log(nums()) //1 console.log(nums()) //2
我们将fn()函数返回的匿名内部函数function赋值给nums这个变量。nums这个函数现在就成为了一个闭包,即使fn()函数执行完毕后,nums函数依然可以访问fn()函数内部的num变量。 但是请注意,每次nums函数执行的时候num变量的值并不会重置为`0`。这是因为每次调用`nums()函数时`,都会为nums()函数创建一个新的作用域,但是这里只给fn()函数创建了一个作用域,又因为num变量是定义在fn函数作用域内部的,所以它在每次nums调用的时候自增而不是被重置。
//函数局部作用域 function fn() { //1.在函数内部定义一个变量 var count = 10 //2.return返回这个f1()函数 return function fn1() { //3.变量减减 count-- console.log(count) } } //4.定义一个变量来接这个fn1()函数 var ff = fn() //5.执行ff,此时这个ff代表的就是f1()函数 ff()
`fn` 函数执行时,JavaScript 引擎会为fn函数创建新的执行上下文和词法环境。该函数执行完毕之后,它会返回 `fn1` 函数并把它赋值给 nums变量。 因此其词法环境如下所示: 由于 `fn1` 函数中没有变量,所以其环境记录将为空。在执行fn1函数期间,JavaScript 引擎将尝试在这个函数的词法环境中查找变量 `num`。 由于 `fn1` 函数的词法环境中没有变量,因此它将查看外部词法环境,即 `fn` 函数仍在内存中的词法环境。JavaScript 引擎找到该变量并将 `num` 打印到控制台
闭包的原理就是作用域链, 因为作用域链就是在当前执行环境下访问了某个变量,如果变量不存在就一直向外层找,直到最终找到最外层也就是全局作用域,就形成了一个链条,也就形成了一个闭包。
setTimeout
//原生的setTimeout传递的第一个函数不能传参 setTimeout(function(param){ alert(param) },1000) //通过闭包可以实现传参效果 function fn1(param){ return function(){ alert(param) } } var ff = fn1(1); setTimeout(f1,1000);
封装变量
//用闭包定义能访问私有函数和私有变量的公有函数。 var counter = (function(){ var privateCounter = 0; //私有变量 function change(val){ privateCounter += val; } return { increment:function(){ //三个闭包共享一个词法环境 change(1); }, decrement:function(){ change(-1); }, value:function(){ return privateCounter; } }; })(); console.log(counter.value());//0 counter.increment(); counter.increment();//2 //共享的环境创建在一个匿名函数体内,立即执行。 //环境中有一个局部变量一个局部函数,通过匿名函数返回的对象的三个公共函数访问。
防抖函数
// 防抖函数,防止频繁触发回调,在 delay 时间内还调用函数会重新计时 const debounce = (callback, delay) => { let timer; • // 返回的函数使用了外部的 timer,使用这里使用了闭包 return function(...args) { clearTimeout(timer); timer = setTimeout(() => { callback.apply(this, args); }, delay); }; }; • // 有什么用呢? // 比如限制用户频繁点击 const clickHandler = () => { console.log('You click the button'); } • // 200 ms 内再次点击会重新计时 200 ms, 如果没有再次点击才会执行 clickHandler const debounceClickHandler = debounce(clickHandler, 200);