什么是闭包
MDN 的解释: 闭包是函数和声明该函数的词法环境的组合.
简单讲, 闭包就是指有权访问另一个函数作用域中的变量的函数.
它由两部分构成: 函数, 以及创建该函数的环境. 环境由闭包创建时在作用域中的所有局部变量组成.
理解闭包的关键在于: 外部函数调用之后其变量对象本应该被销毁, 但闭包的存在使我们仍然可以访问外部函数的变量对象, 这就是闭包的重要概念.
如何产生一个闭包函数
创建闭包最常见方式, 就是在一个函数内部创建另一个函数.
- function outer() {
- let name = "hello"; // 闭包创建时所能访问的局部变量
- function sayHello() { // 闭包函数
- alert(name);
- }
- return sayHello; // 返回闭包函数
- }
- let myFunc = outer();
- myFunc();
闭包的作用域链包含着它自己的作用域, 以及包含它的函数的作用域和全局作用域.
outer 有了 myFunc 的引用, 内存一直得不到释放, 咋办呢? 这样的函数多了是不是会造成内存溢出?
手动释放一下:
myFunc = null;
闭包的注意事项 (如何防止内存泄漏)
通常, 函数的作用域及其所有变量都会在函数执行结束后被销毁. 但是, 在创建了一个闭包以后, 这个函数的作用域就会一直保存到闭包不存在为止.
- function makeAdder(x) {
- return function(y) {
- return x + y;
- };
- }
- let add5 = makeAdder(5);
- let add10 = makeAdder(10);
- console.log(add5(2)); // 7
- console.log(add10(2)); // 12
- add5 = null;
- add10 = null;
add5 和 add10 都是闭包. 它们共享相同的函数定义, 但是保存了不同的词法环境. 在 add5 的环境中, x 为 5. 而在 add10 中, x 则为 10.
最后通过 null 释放了 add5 和 add10 对闭包的引用.
在 JavaScript 中, 如果一个对象不再被引用, 那么这个对象就会被垃圾回收机制回收;
如果两个对象互相引用, 而不再被第 3 者所引用, 那么这两个互相引用的对象也会被回收.
闭包中的 this 对象
- let name = "window";
- let obj = {
- name: 'object',
- getName: function() {
- return function() {
- return this.name;
- }
- }
- }
- obj.getName()(); // Windows
在上面这段代码中, obj.getName()() 实际上是在全局作用域中调用了匿名函数, this 指向了 Windows.
Windows 才是匿名函数功能执行的环境.
如果想使 this 指向外部函数的执行环境, 可以这样改写:
- let name = "window";
- let obj = {
- name: 'object',
- getName: function() {
- var that = this;
- return function() {
- return that.name;
- }
- }
- }
- obj.getName()();
函数内部的定时器
当函数内部的定时器引用了外部函数的变量对象时, 该变量对象不会被销毁.
- (function() {
- let a = 0;
- setInterval(function(){
- console.log(a++);
- }, 1000)
- })()
闭包的用途
模拟块级作用域
- var isShow = true;
- if(isShow){
- var a=1000;
- console.log(a);
- }
- console.log(a); // 在 if 定义的变量在外部可以访问
- (function(){ // a 在外部就不认识啦
- var isShow = true;
- if(isShow){
- var a=10000;
- console.log(a);
- }
- })();
- console.log(a); // 报错, 无法访问
让变量的值始终保持在内存中, 对结果进行缓存
- function fn(){
- let count = 0;
- return function(){
- count++;
- return count;
- }
- }
- let add=fn();
- add(); // 1
- add(); // 2
- add(); // 3
封装工具函数
- let counter = (function(){
- let privateCounter = 0; // 私有变量
- function change(val){
- privateCounter += val;
- }
- return {
- increment:function(){ // 三个闭包共享一个词法环境
- change(1);
- },
- decrement:function(){
- change(-1);
- },
- value:function(){
- return privateCounter;
- }
- };
- })();
- counter.value(); // 0
- counter.increment();
- counter.value(); // 1
经典前端面试题每日更新, 欢迎参与讨论, 地址: https://github.com/daily-interview/fe-interview.
来源: http://www.jianshu.com/p/d1eb9459713f