javascript学习笔记(二)-闭包

来源:互联网 发布:WiFi无法连接网络? 编辑:程序博客网 时间:2024/05/18 00:46

闭包的理解和使用是学习js绕不过去的一道坎,为此笔者在阅读《javascript权威指南》的基础上又参考了几篇博客,终于了解个大概,下面就来和大家分享分享。

参考资料:

《javascript权威指南 第6版》
学习javascript闭包 - 阮一峰
MDN - JavaScript闭包

1. 变量作用域

要理解js中的闭包,就必须了解js的变量作用域。js的作用域有2种,全局变量和局部变量。
全局变量有全局作用域,一经定义就可在任何位置使用。这和静态语言的全局变量有些相似。而局部变量就有些特殊,不同于静态语言(如java、C)的{}作用域,javascript的作用域是函数范围的。

function test(){    //因为函数体内有定义a的地方,只是目前还未赋值,    //所以是undefined。    console.log(a);  //undefined    for (var i = 0; i < 5; i++) {        console.log(i); //0,1,2,3,4        var j=i;    }    var a = 1;    //由于js特殊的函数作用域,在for循环内部定义的    //i、j在循环结束后并没有销毁而是保存下来了    console.log('i='+i); // i=5    console.log('j='+j); // j=4    function inner(){        var inner1 = 123; //内函数的一个变量        // 特殊的函数作用域使得内部函数可以读取外部函数变量        console.log('a='+a);        console.log('i='+i);    }    inner(); // 输出 a=1 ,i=5    //console.log(inner1);}test();

也就是说,只要在一个函数体内定义的局部变量,在整个函数内都是有效的。所以上面例子中,内函数可以读取到外函数的变量,for循环结束后的变量还可以被读取。因为它们都属于同一个函数作用域(test()函数)内。

好了,现在思考这个问题。在内函数inner()定义的inner1这个变量外函数是否可以读取呢?答案是不能的。为什么,不是说函数内的变量都可以读取吗?

因为我们的inner()也是一个函数,正是由于函数作用域的存在,函数外的语句不能访问函数内的变量。所以到现在我们可以得知

  • javascript具有函数作用域
  • 在嵌套函数中,嵌套函数可以访问外函数的变量

2.读取局部变量

上面我们说函数外是读取不到函数内定义的局部变量的。那我们有这个需求怎么办?

最简单的例子,我们想在函数外读取test()函数的i变量

function test(){    var i=0;}// 在此输出i的值

内函数可以读取局部变量,于是

function test(){    var i=0;    function inner(){        return i; //在内函数读取到了i值,并作为返回值返回    }    return inner();}console.log(test()); // 0

到此就解决了,可我们还有不满。现在是返回一个值,要是多个值呢?返回数组又不方便,还有没有别的方法?现在想,既然js可把函数当做返回值,能不能将内函数返回?

function test(){    var i=0;    function inner(){        return i;    }    return inner; //返回一个函数}var a = test(); //a是一个函数console.log(a()); // 0

可行,至于为什么test()函数的局部变量i 没有在test()执行完销毁而在调用a()时还能打印出来,这是下一篇深入闭包原理时讲的内容。现在我们先关注现象。

刚才说,要是函数内有多个局部变量,而要都在函数外访问怎么办?可以这样

function test() {    var v1 = 'v1的值';    var v2 = 'v2的值';    function funcV1() {        return v1;    }    function funcV2(){        return v2;    }    return {       //返回一个对象,并将内函数当做值传入        v1:funcV1,        v2:funcV2    }}var obj = test(); //接收对象值console.log(obj.v1()); //v1的值console.log(obj.v2()); //v2的值

这种写法有点眼熟,对了,在静态语言中(java、C#)读取类内的私有变量不和这很类似吗,在类里有get、set方法。类外调这些方法。不过在js中是读取函数内局部变量。

3.闭包

闭包广泛存在于函数式编程的语言中,维基百科上对闭包的定义是:

闭包是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

可以看出,闭包是一类函数,它能读取创建它时的局部变量(在js中就是内函数能读取外函数的局部变量),这不算稀奇,而稀奇的是,即使它离开了这个环境,它依然保存着这个变量。意味着,这个闭包保存了这些创建时引用的局部变量。

function test(){    var v1 = 1;    return function(){ //返回匿名的内函数        v1++;        return v1;    }}var f = test(); //现在f就是一个闭包函数,由于创建时引用了v1这个局部变量,现在它保存着这个变量/** * 每次调用,v1值加1 */console.log(f()); //2console.log(f()); //3console.log(f()); //4

上面解释的很清楚了,让我们再复杂一点,试着往闭包传参数。

function test(){    return function(a){        a.a1++;    }}var a ={    a1:1}var f = test();//传入一个对象af(a); f(a);f(a);//对象在闭包中被改变console.log(a); // { a1: 4 }

总之,闭包就是访问了它的外部变量的函数,可以用它来模拟私有方法,封装局部变量等。至于其他的特性及原理日后有机会再研究。

0 0