`

Javascript 函数 (七) 闭包【转】

阅读更多

闭包(Closures)闭包属于比较难的一部分,在学习闭包之前,先来学习以下Javascript的作用域(Scope)

作用域链(Scope Chain)

函数内部定义的变量,在函数外不能访问,或者变量定义在代码段中(如if,for中),在代码段之外也不可访问。

Js代码 复制代码
  1. var a =1;   
  2. function f(){   
  3.  var b=1;   
  4.  return a;   
  5. }   
  6. f();//a=1   
  7. b;//b 没有定义  
var a =1;
function f(){
 var b=1;
 return a;
}
f();//a=1
b;//b 没有定义

 a 是全局变量,而b定义在函数f之中。所以:

 在f函数中,a和b都可以访问。

 在f函数之外,a可以访问,而b不可以

 

 再次看个例子

 

Js代码 复制代码
  1. var a = 1;   
  2. function f(){   
  3.  var b = 1;   
  4.  function n(){   
  5.   var c =3;   
  6.  }   
  7. }  
var a = 1;
function f(){
 var b = 1;
 function n(){
  var c =3;
 }
}

 如果定义一个函数n在函数f中,函数n不但可以访问自己作用域的c,而且还能访问所谓的父作用域的b和a

 这就是作用域链(Scope Chain)

 词法作用域(Lexical Scope)

在Javascript中,也有词法作用域(Lexical Scope),这个意思是,函数在定义的时候就生成了它的作用域,而不是在调用的时候,看个例子就明白了。

Js代码 复制代码
  1. function f1(){var a=1;f2();}   
  2. function f2(){return a;}   
  3. f1();//a没有定义  
function f1(){var a=1;f2();}
function f2(){return a;}
f1();//a没有定义

先看看函数f1,调用了函数f2,因为函数局部变量a也在f1中,可能期望函数f2也访问a,但事实却不是这样。

因为这个时候f2函数已经定义完毕了,而它的范围没有a被找到。不管是函数f1还是函数f2,仅仅能访问的是本身的局部变 量或者全局变量。

 

用闭包来破坏这个作用域链(Breaking the Chain with Closure)

让我们举例来说明闭包吧。

实例1

Js代码 复制代码
  1. function f(){   
  2.  var b="b";   
  3.  return function(){   
  4.   return b;   
  5.  }   
  6. }  
function f(){
 var b="b";
 return function(){
  return b;
 }
}

 函数f包含了变量b,在全局作用域中不可能访问b,结果为没有定义(undefined)。

 看看这个函数的返回值为一个函数。这个新的函数可以访问f范围中的变量b。看如下代码

 

Js代码 复制代码
  1. var n = f();   
  2. n();//访问b  
var n = f();
n();//访问b

 由于函数f本身就是全局的,而它返回的函数又是个新的全局函数,而它又可以访问函数f的作用域。

 

实例2

这个例子和上个例子结果是一样的,但是有一点不同,函数f并不返回一个函数,而是新建一个全局变量n,代码如下

Js代码 复制代码
  1. var n;   
  2. function f(){   
  3.  var b = "b";   
  4.  n=function(){   
  5.   return b;   
  6.  }   
  7. }  
var n;
function f(){
 var b = "b";
 n=function(){
  return b;
 }
}

 所以可以直接n()来访问函数f里的变量b

 

通过上面两个例子我们就可以说当一个函数指向了它的父作用域,就可以称之为闭包。

当我们创建了个传递参数的函数f,这个参数就变成了函数f的局部变量了。我们可以创建一个f的内部函数来返回这个参数

Js代码 复制代码
  1. function f(arg){   
  2.  var n =function(){   
  3.    return args;   
  4.  }   
  5.   
  6.  arg++;   
  7.  return n;   
  8. }   
  9.   
  10. var m= f(123);   
  11. m();//124  
function f(arg){
 var n =function(){
   return args;
 }

 arg++;
 return n;
}

var m= f(123);
m();//124

 

闭包在循环中的应用

再循环中很容易引起一些bug,虽然表面是正常的。

看如下的代码

Js代码 复制代码
  1. function f(){   
  2.     var a = [];   
  3.     var i;   
  4.     for(i=0;i<3;i++){   
  5.         a[i] = function(){   
  6.             alert(i);   
  7.             return i;   
  8.         }   
  9.     }   
  10.     return a;   
  11. }   
  12.   
  13. var a= f();   
  14. a[0]();//3   
  15. a[1]();//3   
  16. a[2]();//3  
function f(){
    var a = [];
    var i;
    for(i=0;i<3;i++){
        a[i] = function(){
            alert(i);
            return i;
        }
    }
    return a;
}

var a= f();
a[0]();//3
a[1]();//3
a[2]();//3

新建个循环,我们的目的是每次循环都新建一个函数,函数返回的是循环的序列值也就是i。我们看看以上代码的运行结果

都是为3.而我们期望的结果是1,2,3。

到底是为什么呢?我们新建了三个闭包,都指向了变量i。闭包并没有记住变量i的值,它仅是变量i的引用。在循环结束后,i的值是3,所以结果都是3了。

 

来看看正确的写法

Js代码 复制代码
  1. function f() {   
  2.   var a = [];   
  3.   var i;   
  4.   for(i = 0; i < 3; i++) {   
  5.     a[i] = (function(x){   
  6.       return function(){   
  7.         alert(x);   
  8.         return x;   
  9.       }   
  10.     })(i);   
  11.   }   
  12.   return a;   
  13. }   
  14.   
  15. var a = f();   
  16. a[0]();//0   
  17. a[1]();//1   
  18. a[2]();//2  
function f() {
  var a = [];
  var i;
  for(i = 0; i < 3; i++) {
    a[i] = (function(x){
      return function(){
        alert(x);
        return x;
      }
    })(i);
  }
  return a;
}

var a = f();
a[0]();//0
a[1]();//1
a[2]();//2

 我们又用了一个闭包,把变量i变成了局部变量x了,x变量每次都是不同的值了。如果没有彻底理解自调用函数那就如下写法就可以明白了

Js代码 复制代码
  1. function f() {   
  2.   function makeClosure(x) {   
  3.     return function(){   
  4.       return x;   
  5.     }   
  6.   }   
  7.   var a = [];   
  8.   var i;   
  9.   for(i = 0; i < 3; i++) {   
  10.     a[i] = makeClosure(i);   
  11.   }   
  12.   return a;   
  13. }  
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    JS匿名函数、闭包

    匿名函数,也称为拉姆达函数,是一种使用JavaScript函数的强大方式。以下总结了匿名函数的特点: 任何函数表达式从技术上说都是匿名函数,因为没有引用它们的确定的方式; 在无法确定如何引用函数的情况下,递归...

    JavaScript闭包函数

    闭包是ECMAScript (JavaScript)最强大的特性之一,但用好闭包的前提是必须理解闭包。闭包的创建相对容易,人们甚至会在不经意间创建闭包,但这些无意创建的闭包却存在潜在的危害,尤其是在比较常见的浏览器环境下...

    深度探讨javascript函数的原型链和闭包

    深度探讨javascript函数的原型链和闭包

    深入理解javascript原型和闭包

    深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系

    javascript笔记之匿名函数和闭包.docx

    javascript笔记之匿名函数和闭包.docx

    js高级函数之闭包

    js高级中的函数之闭包函数全解与应用场景(循环闭包,定时器,面试题)

    理解_JavaScript_闭包

    本文结合 ECMA 262 规范详解了闭包的内部工作机制,让 JavaScript 编程人员对闭包的理解从“嵌套的函数”深入到“标识符解析、执行环境和作用域链”等等 JavaScript 对象背后的运行机制当中,真正领会到闭包的实质。

    JavaScript函数、闭包、原型、面向对象学习笔记

    给大家分享了一篇关于JavaScript函数、闭包、原型、面向对象的知识点学习笔记内容,有兴趣的朋友参考下。

    详解JavaScript匿名函数和闭包

    在JavaScript前端开发中,函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在JavaScript,函数在每次创建时生成闭包。匿名...

    深入理解javascript函数参数与闭包

    最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数。本人把学习的过程整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径,避免走弯路。内容有些多...

    js闭包函数

    学习javascript闭包函数的实用文档,讲解全面、详细。pdf格式,

    浅析javascript语言中的函数闭包现象.pdf

    浅析javascript语言中的函数闭包现象.pdf

    javascript笔记之匿名函数和闭包

    本文介绍了js匿名函数和闭包的相关内容,供大家参考,具体内容如下 匿名函数 [removed] //function(){}//会报错 var fun = function(){};//将匿名函数赋值给变量 (function(){})();//匿名函数自执行 function...

    JavaScript函数式编程

    全书共9章,分别介绍了JavaScript函数式编程、一等函数与Applicative编程、变量的作用域和闭包、高阶函数、由函数构建函数、递归、纯度和不变性以及更改政策、基于流的编程、类编程。除此之外,附录中还介绍了更多...

    浅谈Javascript嵌套函数及闭包

    其实是要讲闭包的一些初级应用,但是为了将闭包,我们还是从嵌套函数开始说吧,纵使所有的JavaScript函数都可以说是闭包,但是只有当一个嵌套函数被导出到它所定义的作用域之外时,这种闭包才是有趣的。

    javaScript函数式编程

    全书共9章,分别介绍了JavaScript函数式编程、一等函数与Applicative编程、变量的作用域和闭包、高阶函数、由函数构建函数、递归、纯度和不变性以及更改政策、基于流的编程、类编程。除此之外,附录中还介绍了更多...

    理解javascript函数式编程中的闭包(closure)_.docx

    理解javascript函数式编程中的闭包(closure)_.docx

    深入理解javascript原型和闭包1

    在咱们的第一节(深入理解javascript原型和闭包(1)——一切都是对象)中说道,函数也是一种对象。他也是属性的集合,你也可以对函数进行自定义属性。不用等咱

Global site tag (gtag.js) - Google Analytics