JavaScript 的闭包原理与详解。

JavaScript的闭包是一个特色,但也是很多新手难以理解的地方,阅读过不少大作,对闭包讲解不一,个人以为,在《JavaScript高级程序设计》一书中,解释的最为详尽,结合此书,表述一下我对JavaScript闭包的理解,希望能对新手有些帮助。

闭包的例子

var count = 10; //全局作用域 标记为flag1

   function add() {
       var count = 0; //函数全局作用域 标记为flag2
           return function() {
           count += 1; //函数的内部作用域
           alert(count);
       }
   }
       var s = add()
       s(); //输出1
       s(); //输出2
       
复制代码

来看一下发生了什么吧,add()的返回值是一个函数,首先第一次调用s()的时候,是执行add()的返回的函数,也就是下面这个函数:

function(){
    count+=1;//函数的内部作用域
    alert(count);
}
复制代码

也就是将count+1,在输出,那count是从哪儿来的的呢,根据作用域链的规则,底层作用域没有声明的变量,会向上一级找,找到就返回,没找到就一直找,直到window的变量,没有就返回undefined。这里明显count 是函数内部的flag2 的那个count ,

  var count=10;//全局作用域
	function add(){
            //var count=0;注释掉了
            return function(){
            count+=1;//函数的内部作用域
            alert(count);
           }
      }
            var s=add()
            s();//输出11
	    s();//输出12
           
复制代码

自然这是体现不出闭包的性质,只为了说明函数作用域链
继续说明:第一次执行,是没有疑问的输出1,那第二次的过程是怎样的呢?
继续执行那个函数的返回的方法,还是count+=1;然后再输出count ,这里问题就来了,不应该继续向上寻找,找到count=0;然后输出1吗?不知道有没有注意一个问题,那就是s()执行的是下面这个函数

function(){
    count+=1;//函数的内部作用域
    alert(count);
}
复制代码

而不是

function add(){
     var count=0;//函数全局作用域 标记为flag2
     return function(){
     count+=1;//函数的内部作用域
     alert(count);
   }
}
复制代码

也就是说add(),只被执行了一次。然后执行两次s(),那count的值就是只声明了一次。

var s=add(),函数add 只在这里执行了一次。

下面执行的都是s(),那第二次的count的值是从哪儿来的,没错它还是第一次执行add时,留下来的那个变量。

(这怎么可能,函数变量执行完就会被释放啊,为什么还在?这里就是一个垃圾回收机制的引用计数问题)。

“”如果一个变量的引用不为0,那么他不会被垃圾回收机制回收,引用,就是被调用“”。

由于再次执行s()的时候,再次引用了第一次add()产生的变量count ,所以count没有被释放,第一次s(),count 的值为1,第二次执行s(),count的值再加1,自然就是2了。

让我们返回来再看看,根据以上所说,如果执行两次add() ,那就应该输出 都是1,来改一下这个函数

function add(){
  var count=0;//函数全局作用域
  return function(){
      count+=1;//函数的内部作用域
      alert(count);
  }
}
add()();//输出1
add()();//输出1
复制代码

输出的两次都是1. 不知道通过这个示例,你有没有理解了闭包。 描述一下闭包的结构吧,为什么闭包一般都需要一个匿名函数,为了实现作用域链的规则,需要有两层作用域。 想来大家都应该理解了。 下面再描述一个常见的错误

<p>1</p><p>2</p><p>3</p>
<p>4</p><p>5</p><p>6</p>
		
var plist=document.getElementsByTagName('p');
for (var i=0;i<plist.length;i++) {
      plist[i].onclick=function(){
           alert(plist[i].innerHTML)//全是undefined
            }
        }
        
复制代码

想要点击相应的p 弹出对应的i的值,但是这里发生了什么,点击任意一个数,弹出的都是undefined,我的天,不应该是1,2,3,4,5,6的吗? 解释一下,函数执行完,i 的值是6 ,没有错吧。那plist[6]是不是undefined。 我们点击的时候,触发的就是输出undefined,但我们想要的值是点击对应的p 的innerHTML, alert(this.innerHTML)//万事大吉, this 是一个好东西,指向当前对象当我们绑定的时候绑定的就是当前值,而不是动态的i的值,前面绑定的是一个动态i 的值,这里也可以使用闭包解决,不过不推荐,毕竟闭包使用的话,会让内存无法释放,也就是闭包越多,占的内存越多。使用需谨慎。

总结一下
JavaScript闭包的形成原理是基于函数变量作用域链的规则 和 垃圾回收机制的引用计数规则。
JavaScript闭包的本质是内存泄漏,指定内存不释放。
(不过根据内存泄漏的定义是无法使用,无法回收来说,这不是内存泄漏,由于只是无法回收,但是可以使用,为了使用,不让系统回收)
JavaScript闭包的用处,私有变量,获取对应值等,。。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享