定义闭包

■知识点
函数被调用时,会产生一个临时上下文活动对象,它是函数作用域的顶级对象,作用域内所有私有
变量、参数、私有函数等都将作为上下文活动对象的属性而存在。
函数被调用后,在默认情况下上下文活动对象会被立即释放,避免占用系统资源,但是,当函数内
的私有变量、参数、私有函数等被外界引用,则这个上下文活动对象暂时会继续存在,直到所有外界引
用被注销
但是,函数作用域是封闭的,外界无法访问。那么在什么情况下,外界可以访问到函数内的私有成
员呢?
根据作用域链,内部函数可以访问外部函数的私有成员。如果内部函数引用了外部函数的私有成
员,同时内部函数又被传给外界,或者对外界开放,那么闭包体就形成了,这个外部函数就是一个闭包
体,它被调用后,它的调动对象暂时不被注销,其属性会继续存在,通过内部函数,可以持续读写外部
函数的私有成员。
■实例设计
典型的闭包体是一个嵌套结构的函数。内部函数引用外部函数的私有成员,同时内部函数又被外界
引用,当外部函数被调用后,就形成了闭包,这个函数也称为闭包函数,
下面是一个典型的闭包结构。

  1. function f(x){ //外部函数
  2. return function(y){ //内部函数,通过返回内部函数,实现外部引用
  3. return x + y; //访问外部函数的参数
  4. };
  5. }
  6. var c = f(5); //调用外部函数,获取引用内部函数
  7. console.log(c(6)); //调用内部函数,原外部函数的参数继续存在
  8. console.log(c(6)); //调用内部函数,原外部函数的参数继续存在
  9. console.log(c(6)); //调用内部函数,原外部函数的参数继续存在

图片.png
解析过程简单描述如下:
第1步,在JavaScript脚本预编译期,声明的函数f和变量c,先被词法预解析;
第2步,JavaScript执行期,调用函数f,并传入值5
第3步,在解析函数f时,创建执行环境(函数作用域),创建活动对象,将函数的参数,私有变量,内部函数映射为活动对象的属性。
第4步,参数x的值为5,映射为活动对象的x属性;
第5步,内部函数,通过作用域链,引用了参数x,但是还没有被执行。
第6步,外部函数被调用后,返回内部函数,导致内部函数被外界变量c引用
第7步, JavaScript解析器检测到外部函数活动对象的属性被外界引用,无法注销该活动对象,于
是在内存中继续维持该对象的存在。
第8步,当调用c,即调用内部函数时,可以看到外部函数的参数x存储的值继续存在,于是也就
可以实现后续运算操作,返回x+y=5+6=11
■小结
下面的结构形式也可以形成闭包:通过全局变量引用内部函数,实现内部函数对外开放。

  1. var c; //声明全局变量
  2. function f(x){ //外部函数
  3. c = function(y){ //内部函数,通过向全局变量开放实现外部引用
  4. return x + y; //访问外部函数的参数
  5. };
  6. }
  7. f(5); //调用外部函数
  8. console.log(c(6)); //使用全局变量c调用内部函数,返回11
  9. console.log(c(6)); //使用全局变量c调用内部函数,返回11
  10. console.log(c(6)); //使用全局变量c调用内部函数,返回11