zoukankan      html  css  js  c++  java
  • 深入理解闭包 Minoz

    闭包之前一直都在看,却总感觉没有深入理解,一直处于云里雾里,今天终于可以炫耀的说我懂了!

    闭包是什么?

    官方解释我就不说了,只说我理解的吧~

    闭包是提供给外部访问函数内部私有变量的一个接口

    一个函数里定义另一个函数就会产生闭包

    解释一下:

    function func() {
        var a = 10;
        return function() {
            return a;
        };
    }
    var b = func();
    console.log(b());

    在函数里定义变量都是私有的,外面无法访问到函数内部,那么,就可以通过闭包,将私有变量返回,外界就可以访问到func函数里的变量了。所以,我说闭包是提供给外部访问函数内部私有变量的一个接口。

    了解闭包的作用域链

    此处已经在我的博客的上一篇深入理解作用域说过了,大家可以参考,这里就不再累赘。

    闭包注意的几个小问题

     我们来看几道题逐渐理解闭包

    1.循环中的闭包

    1>请注意第6行

     1 function func(list) {
     2     var arr = [];
     3     for(var i = 0; i < list.length; i++) {
     4         var index = i;
     5         arr.push(function(){
     6             console.log('index为' + index); //index
     7             console.log('list为' + list[i]);
     8         });
     9     }
    10     return arr;
    11 }
    12 function output() {
    13     var list = [1, 2, 3];
    14     var all = func(list);
    15     for(var i = 0; i < all.length; i++){
    16         all[i]();
    17     }
    18 }
    19 output();

    结果为3个    index为2   list为undefined

    2>请注意第6行

     1 function func(list) {
     2     var arr = [];
     3     for(var i = 0; i < list.length; i++) {
     4         var index = i;
     5         arr.push(function(){
     6             console.log('i为' + i);  //i
     7             console.log('list为' + list[i]);
     8         });
     9     }
    10     return arr;
    11 }
    12 function output() {
    13     var list = [1, 2, 3];
    14     var all = func(list);
    15     for(var i = 0; i < all.length; i++){
    16         all[i]();
    17     }
    18 }
    19 output();

    结果为3个    i为3   list为undefined

    结果是不是很出乎意料,之前我因为这个问题纠结了好长时间,只怪当初太年轻~~

    在for循环中的使用闭包调用的是同一个闭包,因此无论for循环几次,都是最后一个值。那么,为什么是undefined呢?

    这里不得不提到javascript中没有块级作用域,因此虽然看似是在for循环中声明了变量 i,但实际上i的作用域是func函数。并且闭包中局部变量是引用并不是

    拷贝,因此,当退出循环时i为3,闭包中的i也就为3,list[3](list[2]为最后一个值)不存在,所以为undefined。

    将上述第7行代码改为下面的代码

    1 console.log('list为' + list[index]); //list[index]

    将输出  list为3

    index值自然是for循环中最后的一个i值为2。

    解决办法

    知道是什么原因了,那如何解决呢?

     1 function func(list) {
     2     var arr = [];
     3     for(var i = 0; i < list.length; i++) {
     4         var index = i;
     5         arr.push(
     6             (function(j){
     7             console.log('list为' + list[j]);
     8             })(i)
     9         );
    10     }
    11 }
    12 func([1, 2, 3]);

    输出  list为1  list为2  list为3

    添加另一个闭包,并传参使其立即执行,参数是按值传递的,所以每一次传过去的值都是不同的值,得出我们想要的答案。

    2.闭包中的this对象

     1 var num = 10;
     2 var obj = {
     3     num: 'nana',
     4     getNum: function() {
     5         return function(){
     6             return console.log(this.num);
     7         }
     8     }
     9 }
    10 obj.getNum()();

    输出结果为:10

    是不是又很神奇,在闭包中this是指向window的,所以,当我们访问this.a时,访问的是全局变量a。那么,想要访问函数func中的a呢?

    解决办法

     1 var num = 10;
     2 var obj = {
     3     num: 'nana',
     4     getNum: function() {
     5         var that = this;
     6         return function(){
     7             return console.log(that.num);
     8         }
     9     }
    10 }
    11 obj.getNum()();

    输出结果为:nana

    3.变量回收

    1 function func() {
    2     var a = 0;
    3     return function(){ 
    4         return ++a;
    5     };
    6 }
    7 var f = func();
    8 console.log(f());
    9 console.log(f());

    输出结果:1    2

    在函数中的局部变量会随着函数执行完毕而被回收,可是,func函数引用着闭包函数,闭包中引用着局部变量a,因此,变量不会被js回收机制回收。第一次

    调用f时a加1,第二次调用时,由于a未被回收,所以又在1的基础上又加了1为2。

    那么,js中回收机制是怎么工作的呢?

    在js中如果对象不再引用,则会被回收,如果两个对象互相引用不再被第三者引用也会被回收,但如果还被第三者引用就不会回收,上述由于处于这种情况所

    以变量a不会被回收。

    由于无法回收局部变量,会导致性能问题,因此,要避免滥用闭包,在闭包使用完毕时,需删除局部变量。

    4.多个函数绑定同一闭包

     1 function func() {
     2     var num = 10;
     3     getNum = function() {
     4         console.log(num);
     5     };
     6     countNum = function() {
     7         num++;
     8     };
     9     setNum = function(x) {
    10         num = x;
    11     };
    12 }
    13 func();
    14 getNum();
    15 countNum();
    16 getNum();
    17 setNum(20);
    18 getNum();

    输出结果为10   11    20

    5.闭包外部函数所有局部变量都在闭包内,声明在闭包函数后的局部变量也可访问

    1 function func() {
    2     function a(){
    3         console.log(num);
    4     };
    5     var num = 10;
    6     return a;
    7 }
    8 func()();

    结果输出  10

    6.函数调用时访问的是不同的闭包

     1 function func(list) {
     2     var arr = [];
     3     for(var i = 0; i < list.length; i++) {
     4         var index = i;
     5         arr.push(
     6             (function(j){
     7             console.log(list[j]);
     8             })(i)
     9         );
    10     }
    11 }
    12 
    13 func([1, 2, 3]);
    14 func([6, 7, 8]);

    输出结果1,2,3   6,7,8

    上面举了这么多例子,每个例子都不同,如果想完全理解的话只有当你遇到上述问题时,才会让你印象深刻,所以还是乖乖的去敲代码吧!

    最后来个小测试

    1 for(var i = 0; i < 5; i++) {
    2     var index = i;
    3     (function(j){
    4         setTimeout(function(){
    5             console.log(j);
    6         },10);
    7     })(i)
    8 }

    输出结果 0 1 2 3 4

    1 for(var i = 0; i < 5; i++) {
    2     var index = i;
    3     (function(j){
    4        setTimeout(function(){
    5             console.log(index);
    6         },10);
    7     })(i)
    8 }

    输出结果 4 4 4 4 4

    1 for(var i = 0; i < 5; i++) {
    2     var index = i;
    3     (function(j){
    4         setTimeout(function(){
    5             console.log(i);
    6         },10);
    7     })(i)
    8 }

    输出结果 5 5 5 5 5

    猜对了么?如果没有,就再看看上面例子吧~

  • 相关阅读:
    Linux 设备驱动--- Poll 方法 --- Select【转】
    Linux 设备驱动--- 阻塞型字符设备驱动 --- O_NONBLOCK --- 非阻塞标志【转】
    一篇不错的v4l2入门文档【转】
    戴文的Linux内核专题:03 驱动程序【转】
    戴文的Linux内核专题:02 源代码【转】
    使用 Python 开始你的机器学习之旅【转】
    双缓冲(Double Buffer)原理和使用【转】
    内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages【转】
    几种常见的YUV格式--yuv422:yuv420【转】
    C程序编译过程浅析【转】
  • 原文地址:https://www.cnblogs.com/nalixueblog/p/4474738.html
Copyright © 2011-2022 走看看