javascript学习笔记(二)-闭包

闭包的理解和使用是学习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()); //2
console.log(f()); //3
console.log(f()); //4

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值