浅谈闭包

闭包的定义

闭包就是可以读取其余函数内部变量的函数。javascript

例如在javascript中,只有函数内部的子函数才能读取局部变量,因此闭包能够理解成“定义在一个函数内部的函数“。java

在本质上,闭包是将函数内部和函数外部链接起来的桥梁。segmentfault

为何要使用闭包

先介绍一下全局变量和局部变量的优缺点bash

全局变量:能够重用、可是会形成全局污染并且容易被篡改。闭包

局部变量:仅函数内使用不会形成全局污染也不会被篡改、不能够重用。异步

因此,全局变量和局部变量的优缺点恰好相对。闭包的出现正好结合了全局变量和局部变量的优势。函数

什么时候使用闭包

但愿重用一个对象,可是又保护对象不被污染篡改时。ui

闭包产生的缘由

官方解释:spa

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(一般是一个函数),于是这些变量也是该表达式的一部分。code

从这里能够看出闭包与环境有关,而与环境扯上关系就离不开做用域,然而JS做用域中特殊的就是词法做用域,这个词法做用域又称之为静态做用域或者闭包。

静态做用域就是函数声明时,就已经定好的做用域,之后也不会改变的做用域。

而JS的语言特性在JS代码运行的时候就已经把一切都定死了,做用域什么的都j定好了,闭包也随之而产生。

闭包是JS语言的一种特性,闭包一般是一个函数,函数是一个独立的做用域,独利的做用域外部环境没法访问,就是;封闭本身的词法做用域,函数有许多特殊形式的函数,这就成就了。包的东西不一样,可是它可以引用到外部函数的成员变量,必定是它包的东西。

能够理解为,可以引用外部函数的成员变量,那它就必定是闭包。

这里有个闭包的详细图解

闭包的表现形式

  • 返回一个函数
  • 做为函数参数传递
  • 回调函数
  • 非典型闭包IIFE(当即执行函数表达式)

返回一个函数:这种形式的闭包在JS中很是很是常见。

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

做为函数参数传递:不管经过何种手段将内部函数传递到它所在词法做用域以外,它都会持有对原始做用域的引用,不管在何处执行这个函数,都会产生闭包。

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

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

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

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

IIFE:IIFE(当即执行函数表达式)并非一个典型的闭包,但它确实建立了一个闭包。

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?(方法越多越好)
复制代码

代码分析

for循环建立了5个定时器,而且定时器是在循环结束后才开始执行

for循环结束后,用var i定义的变量i此时等于6

依次执行五个定时器,都打印变量i,因此结果是打印5次6

**第一种改进方法:**利用IIFE(当即执行函数表达式)当每次for循环时,把此时的i变量传递到定时器中

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

第二种改进方法:setTimeout函数的第三个参数,能够做为定时器执行时的变量进行使用

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

第三种改进方法(推荐):在循环中使用let i代替var i

for(let i=1;i<=5;i++){
  setTimeout(function timer(){
    console.log(i)
  }, 0)
}
复制代码
相关文章
相关标签/搜索