闭包对于学习javascript的同学们来说,是不可避免要谈到的一个概念。

开始接触javascript的时候,对它也是很难理解的,令人生畏,一直疑问,到底什么才是闭包呢?那么我们如何能快速熟练地掌握javascript闭包呢?

相信大家在网上看过不少关于闭包的资料。

那么闭包真的有那么可怕吗?它没有想象中的可怕,其实,要理解闭包我们只需学会三个基本的事实即可。

1、js允许你引用在当前函数以外定义的变量

2、即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。

3、闭包可以更新外部变量的值。

下面分别用实例代码来说明以上三个事实。

一、第一个事实:js允许你引用在当前函数以外定义的变量

function studyClosure(){
    var myName="闭包";
    function study(face){
          return myName+face;
    }
    return study("你好啊");
}
studyClosure(); // "闭包你好啊"
myName是定义在外部studyClosure函数内的变量,也就是说myName是study函数以外的变量。请注意study是如何引用到的。

二、第二个事实:即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。
function studyClosure(){
    var myName="闭包";
    function study(face){
     return myName+face;
    }
    return study;
}
var b=studyClosure();
b("你好啊");  // "闭包你好啊"
b("是谁?");  // "闭包是谁?"
本例子中,在studyClosure()函数中返回的是study函数本身,而不像第一个例子中立即调用study(“你好啊”)。因此,b的值为内部的study(),也就是说调用b函数其实就是调用study函数。那么即使studyClosure函数已经返回了,study函数仍能记住myName的值。
对于这个,它的原理又是怎样的?
原理:js的函数值包含了比调用它们时执行所需要的代码还要多的信息。而且js函数值还在内部存储它们可能会引用的定义在其封闭作用域的变量。那些在其所涵盖的作用域内跟踪变量的函数被称为闭包。study函数就是一个闭包。其代码引用了两个外部变量:studyClosure和myName。每当study被调用时,其代码都能引用到这两个变量,因为该闭包存储了这两个变量。
函数可以引用在其作用域内的任何变量,包括参数和外部函数变量。我们可以利用这点来编写更加通用的studyCloseure函数。
function studyClosure(myName){
    function study(face){
         return myName+face;
    }
    return study;
}
var a=studyClosure("闭包");
a("你好啊");   //"闭包你好啊"
a("是谁?");   //"闭包是谁?"
var b=studyClosure("bibao");
b("a123");    //"bibaoa123"
b("a234");   //"bibaoa234"
本例子我们创建了a和b两个完全不同的函数。尽管他们都是由相同的study函数定义的,但是他们是两个截然不同的对象。第一个函数的myName的值为’闭包’,而第二个函数的myName的值为’bibao’。
闭包是js最优雅、最有表现力的特性之一,也是许多惯用法的核心。js还提供了一种更为方便构建闭包的字面量语法–函数表达式。
function studyClosure(myName){
    return function(face){
           return myName+face;
    }
}

请注意,该函数表达式是匿名的。由于我们只需要其能产生一个新的函数值,而不打算在局部调用它,因此根本没有必要给该函数命名。


三、第三个事实:闭包可以更新外部变量的值。
实际上,闭包存储的是外部变量的引用,而不是它们的值的副本。因此任何具在访问这些外部变量的闭包,都可以进行更新。一个简单的惯用法box对象说明了这一切。它存储了一个可读写的内部值。
function box(){
    var val=undefined;
    return {
        set:function(newVal){val=newVal},
        get:function(){return val},
        type:function(){return typeof val}
    }
}
var b=box();
b.type();//"undefined"
b.set(88.8);
b.get();//88.8
b.type();//"number"
这个例子里产生了一个包含三个闭包的对象。这三个闭包是set,get和type属性。它们共享访问val变量。set闭包更新val的值,随后调用get和type查看更新的结果。

提示:
  1. 函数可以引用定义在其外部作用域的变量
  2. 闭包比创建它们的函数有更长的生命周期
  3. 闭包在内部存储其外部变量的引用,并能读写这些变量


本文章是摘自Effective JavaScript第11条之熟练掌握闭包,是学习的一个笔记记录。版权归原作者所有,只用于交流与学习的目的。