头疼的闭包

Posted apprehensive

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了头疼的闭包相关的知识,希望对你有一定的参考价值。

 

头疼的闭包

 

一.官网的解释

闭包(closure)是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包就是一个函数把外部的那些不属于自己的对象也包含(闭合)进来了

 ·.· 在js中,只有函数内部的子函数才能读取局部变量

 ·.· 闭包就是能够读取其它函数内部变量的函数

 .·.闭包是个子函数

 

   在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

 

 二.如何理解

变量的作用域 

Js中变量的作用域分两种:全局变量和局部变量。 

(1)函数内部可以直接读取全局变量。  

var n = ‘叼你啦!死机‘;
function f1() {
     console.log(n);
}
f1();

 

 (2)在函数外部自然无法读取函数内的局部变量。 

function f1() {
var n = ‘叼你啦!死机‘;
    
}
 console.log(n); //error

 

 此外,函数内部声明变量时,一定要用var关键字来命名变量。否则,就声明了一个全局变量。

 

 当我们需要从外部读取函数内的局部变量,可以在函数的内部再定义一个函数。

function f1(){
     n = ‘叼你啦!死机‘;
     function f2(){
       console.log(n);
     }
 }

 

如上,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2是可见的。(重点一

这就是javascript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。

 

function f1(){
    n = ‘叼你啦!死机‘;
    function f2(){
        console.log(n);
    }
    return f2;
}
var result=f1();
result(); // 叼你啦!死机‘

 

如上,既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在f1外部读取它的内部变量(重点二

 

三.用途

1.读取函数内部的变量

2.令这些变量的值始终保持在垃圾(内存)回收机制中。

(或者这样理解:让这些变量的值始终保持在内存中,不会在f1调用后被自动清除)

 

function f1(){
    var n = ‘叼你啦!死机‘;
    add=function(){
        n = ‘你知道就好‘;
    };
    function f2(){
        console.log(n);
    }
    return f2;
}
var result=f1();
result(); // 叼你啦!死机
add();
result(); // 你知道就好

 

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次是:叼你啦!死机,第二次是:你知道就好。

换另外一个例子会直接说明点

 function f1(){
    var n=999;
    add=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  add();
  result(); // 1000

 在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。重点三

为什么会这样呢?

原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

 

 

前面两个例子中:在add前面没有使用var关键字,因此add是一个全局变量,而不是局部变量。其次,add的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以add相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

 

引用其他文章

作者:大水
链接:https://www.zhihu.com/question/34547104/answer/59515735
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

每次定义一个函数,都会产生一个作用域链(scope chain)。当JavaScript寻找变量varible时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性;这个过程会持续直至找到这个属性或者最终未找到引发错误为止

看个简单版的例子:

(function(){
    var hello="hello,world";
    function welcome(hi){
        alert(hi);        //解析到作用域链的第一个对象的属性
        alert(hello);    //解析到作用域链的第二个对象的属性
    }
    welcome("It‘s easy");
})();

 

运行结果很简单,一个弹窗It‘s easy.一个弹窗hello,world。
分析过程如下:
对于函数welcome(),定义welcome的时候会产生一个作用域链对象,为了表示方便,记作scopechain。scopechain是个有顺序的集合对象。
  • scopechain的第一个对象:为了方便表示记作sc1, sc1有若干属性,引用本函数的参数和局部变量,如sc1.hi ;
  • scopechain的第二个对象:为了方便表示记作sc2,sc2有若干属性,引用外层函数的参数和局部变量,如sc2.hello;
  • ...
  • scopechain的最后一个对象:为了方便表示记作scn,scn引用的全局的执行环境对象,也就是window对象!,如scn.eval();

这里之所以可以弹出hello,world,原因就是变量解析时在welcome函数作用域链的第一个对象上找不到hello属性,然后就去第二个对象上找去了(结果还真找到了)。

所以,JavaScript中的所谓的高大上的闭包其实很简单,根本上还是变量解析。而之所以可以实现,还是因为变量解析会在作用域链中依次寻找对应属性的导致的。

 

以上是关于头疼的闭包的主要内容,如果未能解决你的问题,请参考以下文章

python中对 函数 闭包 的理解

Javascript学习日志:闭包

JS---闭包

scala编程——函数和闭包

爬虫遇到头疼的验证码?Python实战讲解弹窗处理和验证码识别

由于lambda闭包或调度程序问题,程序可能会挂起