JavaScript 我理解的闭包:概念和基础使用

闭包的浮夸

JavaScript闭包,据说是一个前端开发,打开瓶颈的必经之门,它无处不在,只是没有深刻的回望和凝视;
当然了解闭包不易过早、不易过迟,在你学习或者开发一段时间的js之后在了解最佳,但是不要在你使用模块之后再来学习,那样太迟了。闭包分两篇写第一篇概念和基础使用,第二篇 《闭包之模块》

从定义开始

官方文档定义:
函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包

理解定义:
有权限访问另一个函数作用域中变量的函数,就可以称之为闭包
函数在当前词法坏境之外执行
创建闭包就是在一个函数中创建另一个函数
用代码来解释一下看看:

function init() {
  var name ="木子聊前端"; // init 方法的一个局部变量
  function showName(){  // 内部函数方法 一个闭包 
    console.log(name);  // 木子聊前端
  }
  showName();
}
init();
showName(); // 执行报错

init() 函数创建了一个变量name 和一个名叫showName()函数,showName定义在init方法里面,本身没局部有变量,但是可以访问 父级 init 的变量name,而且showName()函数只能在init方法里面被调用;

那么这个是闭包吗?

从技术上讲也许是,但是仔细看定义确切的说并不是。因为我们在函数之外调用 showName()方法报错。不能完全解释闭包的定义,不能很好的理解闭包。需要理解闭包是是隐藏在代码之后的神秘身影,改造一下代码,更能能清晰的解释闭包

function init() {
  var name ="木子聊前端"; 
  function showName(){  
    console.log(name);  
  }
  return showName;
}
var showName = init(); // 不输出任何东西
showName(); // 木子聊前端 -----这个就是闭包

此时showName 能够访问 函数内部 ini() 方法的变量 name属性,符合定义;
和上一个例子不同之处就在与在执行showName方法之前,是通过init内部方法返回。
为什么showName 能够在外部被执行
通常意义上说函数执行完毕,内部变量在内存中地址就被销毁或者收回,这个是JavaScript 垃圾回收机制的原理所导致的;
但是在这里预想的结果没有出现,init执行完毕,局部变量的作用依然存在,原因就是闭包这个神奇的物种,阻止了这件事情的发生;
拜showName 申明的位置,拥有 init函数内部作用域的闭包,使得该作用域一直存在等待 showName()方法随时调用

此时showName()函数一直持有该作用域的引用,而这个引用就是闭包
了解闭包的形成和创建闭包之后该怎么用呢?对写js代码有什么提升?

闭包和循环、变量的应用

先看一个for循环中变量的例子:

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

期待的结果是每隔一秒输出一个数组从0-5 ,然而实际情况是全部输出6,

这是为什么呢?

首先6从哪里来?这个循环的条件是是i<=5 ,也就是说首次成立的条件是6,因此输出的循环是6,是因为定时器的原因吗?循环之后才执行,事实上setTimout(…,0)就算是0,结果也一样。
那那么到底是什么机制呢?
其实这应该是一个缺陷或者说是一个副作用,即闭包只能取得函数任何变量的最后一个值,闭包不保存某个特殊的值,而是一个整个变量对象,至此,它需要有自己的变量,用来在每个迭代中储存 i 的值:

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

这样每个函数即闭包就返回了自己对应的索引;我们还可以优化一下代码:

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

同样传递进去生产一个新的作用域。问题完美解决

闭包和块级作用域 天下无敌

其实闭包想实现的技术就是块级作用域;改写上面的代码,惊奇发现:

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

没有多余完美解决。
let 在for循环中有个特殊用途,每次申明都是上一次的结果,而且left就是一个块级作用域

下一篇继续 写应用关于模块
《闭包之模块》

你可能感兴趣的:(javascript)