闭包

434 阅读2分钟

图解闭包:segmentfault.com/a/119000001…

什么是闭包

在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。

闭包就是能够读取其他函数内部变量的函数.

为什么要使用闭包

全局变量:可以重用、但是会造成全局污染而且容易被篡改。

局部变量:仅函数内使用不会造成全局污染也不会被篡改、不可以重用。

闭包的出现正好结合了全局变量和局部变量的优点。

什么时候使用闭包

重用一个对象,但是又保护对象不被污染篡改

闭包产生的原因

闭包是JS语言的一种特性,闭包通常是一个函数,函数是一个独立的作用域,独立的作用域外部环境无法访问,就是闭;封闭自己的词法作用域,函数有许多特殊形式的函数,这就成就了包。包的东西不同,但是它能够引用到外部函数的成员变量,一定是它包的东西。 可以理解为,能够引用外部函数的成员变量,那它就一定是闭包。

闭包的四种变现形式

1、返回一个函数 2、作为函数参数传递 3、回调函数 4、非典型闭包IIFE(立即执行函数表达式)

1、返回一个函数

var a  = 1;
function foo(){
  var a = 2;
  // 这就是闭包
  return function(){
    console.log(a);
  }
}
var bar = foo();
// 输出2,而不是1
bar();

2、作为函数参数传递

var a=1;
function foo(){
    var a=2;
    function baz(){
        console.log(a);
    }
    bar(baz);
}
function bar(fn){
    //这就是闭包
    fn();
}
//输出2,而不是1
foo();

无论通过何种手段将内部函数传递到它所在词法作用域之外,它都会持有对原始作用域的引用,无论在何处执行这个函数,都会产生闭包。

3、回调函数

//定时器
setTimeout(function timeHandler(){
    console.log('timer');
},100)

//事件监听
$('#container').click(function(){
    console.log('DOM Listener');
})

在定时器、事件监听、Ajax请求、跨窗口通信、Web Workers或者任何异步中,只要使用了回调函数,实际上就是在使用闭包

4、立即执行函数表达式

创建了一个闭包

var a = 2;
(function IIFE(){
  // 输出2
  console.log(a);
})();

经典问题

for(var i = 1; i <= 5; i ++){
  setTimeout(function timer(){
    console.log(i)
  }, 0)
}
//为什么会全部输出6?如何改进,让它输出1,2,3,4,5?(方法越多越好)

输出1,2,3,4,5的方法:

1、立即执行函数表达式,当每次for循环时,把此时的i变量传递到定时器中

for(var i=1;i<=5;i++){
  (function(j){
    setTimeout(function timer(){
      console.log(j)
    },0)
  })(i)
}

2、setTimeout函数的第三个参数,可以作为定时器执行时的变量进行使用

for(var i=1;i<=5;i++){
  setTimeout(function timer(j){
    console.log(j)
  }, 0, i)
}

最推荐的方法

for(let i=1;i<=5;i++){
  setTimeout(function timer(){
    console.log(i)
  }, 0)
}