闭包几乎是通往高级前端工程师所必须通过的一个门槛。在这方面看过不少资料,感受许多文章都只是提到了闭包的实现,而没有涉及闭包的原理。前端
在谈闭包以前须要先了解几个相关的概念:浏览器
一、变量做用域:全局变量拥有全局做用域,在js代码的任何地方均可见。在函数内部声明的变量只在该函数体内可见,被称为局部变量。前端工程师
二、函数做用域:在函数内声明的全部变量在该函数体内始终是可见的。闭包
三、做用域链:做用域链是一个对象。做用域链中定义了该做用域内的变量,它保证了该做用域中变量、函数的有序访问。框架
四、做用域链的建立:定义一个函数时,会保存一个做用域链。调用一个函数时,会建立一个新的对象来存储其局部变量,并将该对象添加至保存的做用域链上,同时建立一个新的更长的表示函数调用做用域的链。对于嵌套函数来讲,每调用一次外部函数,都会建立一个新的做用域链,因此每次调用外部函数,虽然其内部代码相同,可是调用的做用域链是不一样的。函数
五、垃圾回收机制:js具备自动垃圾回收机制,会按期把再也不使用的变量销毁,释放其占用的内存。(只会销毁局部变量,全局变量的生命周期只有在页面或浏览器关闭时才会结束)对象
闭包的原理:函数的执行依赖于函数定义时的做用域链,即js函数执行时用的做用域链是该函数定义时建立的做用域链。大多数时候定义函数时的做用域链在函数执行时仍然有效。可是若是定义函数时的做用域链和执行函数时的做用域链不一样时,就会出现问题。即当一个函数中嵌套了另外一个函数,并把嵌套的函数对象做为返回值返回时,就会产生这种状况。blog
第一个例子很容易理解。可是第二个,能够看到当外部函数把嵌套函数做为返回值返回时,其执行结果仍然是局部变量的值,而不是全局变量的值。这就是由于函数执行时用到的做用域链,实际上是函数定义时建立的。无论该函数在什么时候何地执行,它经过做用域链最早找到的变量就是相同外部函数中定义的变量。而闭包正是利用这种特性实现的:闭包能够捕捉到局部变量或参数,并一直保存下来,使其不会被当成垃圾回收。生命周期
闭包和垃圾回收的关系:调用函数时,会建立一个对象来保存其局部变量,而且把这个对象添加到做用域链上。当函数返回时(即执行完成了),就会从做用域链中把绑定局部变量的对象删除,这个对象就会被看成垃圾回收。可是若是定义了嵌套函数,那么嵌套函数也会有本身相应的做用域链,它与外部函数同样,把其局部变量保存到一个对象上,若是嵌套函数是做为返回值返回的或者存储在某处的属性里,那么就会有一个外部引用指向这个嵌套函数,那么外部函数就不会被看成垃圾回收,它在做用域链中绑定局部变量的对象也不会被回收。ip
闭包的应用场景:实现私有成员;保护命名空间;避免污染全局变量;可使变量长期驻留在内存中。这是在网上找的,不是特别好理解。其实能够参考jQuery的源码,它就是将全部的代码封装到了一个闭包里边的。下边是我以前写的一段代码,再根据闭包进行的改写:
在改写后的代码中,定义了2个闭包,这2个闭包共享同一个变量defaultSize。能够看出使用闭包对代码的封装,避免全局变量的污染有很大的做用。
使用闭包的注意事项:固然闭包也不是十全十美,它能够把局部变量长期保存到内存中,不被看成垃圾回收,这种特性若是使用不当,很容易会形成内存泄漏。建议仍是多看看其它比较成熟的框架是怎么使用闭包的,会对提升我的代码的质量有很大的帮助。