Javascript中的闭包与作用域

JavaScript的闭包与作用域

如果转载或引用,请声明出处

要解决的问题是:从方法A中使用方法B里面定义的变量

闭包:是一个函数,能够在函数生成时捕获附近的“值”
- 参照书本给出的闭包例子

function B() {
    var s = 0;
    return function() {
        return A(s);
    }
};
function A(s) {
    s++;
    console.log(s);
}
var b = B();
b();
输出: 1
[Finished in 0.2s]

很满意,能修改到s的值,原因是在函数生成的时候捕获了附近的“值”,虽然说的有点那么不令人满意,但是能说明问题,s被使用了。然后

  • 再添加往源代码插入几条语句
...
var b = B();
b();
b();//加入3条b();
b();
b();
....
1
1
1
1
[Finished in 0.2s]

这就很奇怪了啊,为什么啊,为什么总是1啊,并应该是每次都加1吗。我也不知道啊

  • 添加add方法并执行对比
var s = 10;
function B() {
    var s = 0;
    add = function() {
        s++;
        console.log(s);
    }
....
};
function A(s) {
....
}
var b = B();
b();
b();
add();
add();
add();
输出 
1
1
1
1
1
2
3
[Finished in 0.2s]

这就更奇怪了,如果说b()方法每次都只能为1,那么add方法应该也只能返回1。另外一个问题是,注意到add方法是一个全局方法,如果调用的话,按照变量由调用处往上搜索的话,应该搜索到s的值应该为10;
结论一:由此可以看出在add函数生成的时候,产生了一个闭包,该闭包捕获了该函数生成的时候的s的值,即为(var s=0)。

  • 那么,该函数是什么时候生成的呢??
    注意到,在代码中存在
...
var b = B();//这里是函数生成的地方
...
b();
b();

故在此处为函数B生成的地方,由于add方法也在B的里面,所以也是add生成的地方,另外注意到,add方法为全局方法,如果没有var b = B();那么函数应该也能被执行,因为在代码的最开头存在var s =10;,那么是否是这样的呢。代码如下

var s = 10;

function B() {
    var s = 0;
    add = function() {
        s++;
        console.log(s);
    }
    return function() {
        return A(s);
    }
};

function A(s) {
    s++;
    console.log(s);
}
// var b = B();
// b();
add();
add();
add();

执行结果如下:

add();
^
ReferenceError: add is not defined

结果是add方法not defined。
结论二:add方法虽然是全局方法里面,但是它是存在与B()中的,只有B()被执行了,才相当于add方法被定义了,否则是没有定义的。
附:最开始自己的认为是B()方法没有被定义,故不存在上下文。。瞎猜的,欢迎指正。

  • 一个新的测试
var b = B();
b();
b();
b();
add();
add();
add();
输出
1
1
1
1
2
3
[Finished in 0.2s]

又回到了最初的起点,呆呆地站在Js前。。。怎么办。难不成这两个用的s不是同一个东西??果然。。

  • 翻山越岭的add方法寻找s之路
    add方法在寻找s的时候,由于在B()方法中生成,首先寻找的是B所在的作用域,这样就能寻找到(var s=0),进而对其++操作,执行3次为1,2,3。此处如果将var s=0注释掉,则会寻找到最外面的var s=10这个s,这时候的作用域就到了全局作用域了,得到的结果应该是11,12,13。
    结论三: 在寻找变量的时候,总是从最靠近函数生成的时候的作用域开始寻找,如果没找到则向上寻找,直到找到或者到达顶层(object—->undefined)

  • b()方法的寻找s之路
    function B是典型的闭包的写法,通过函数参数返回的方式将变量在执行完语句后仍然能被访问。正如上面书输出的数据所显示的那样。但是存在问题。

  • 那么问题来了,为什么add方法就可以而通过闭包的方式每次改变的都是最开始的值(s=0);
    真的是只能说回到了最初的起点,哎。说起来都心塞。
    在JavaScript高级程序设计中,就是那本好大好大本的红色书,人民教育出版社第三版的第70页,在传递参数章节中写着这样的一段话,结论四:“ECMAScript中所有函数的参数都是按值传递的,基本类型值的传递如同基本类型变量的复制一样,引用类型变量的传递就如同引用数据类型变量的复制一样
    所以一目了然,这就解释了为什么会导致使用b()的时候是这样的结果,因为s为基本Number类型。
    如果非要用s呢,怎么办?
  • 将基本类型的值传递改为引用类型的值传递
var s = 10;

function B() {
    // "use strict";s
    var s = 0;
    var c = {
        value: 0
    };
    add = function() {
        s++;
        console.log('add...' + s)
    }
    return function() {
        return A(c);
    }
};

function A(c) {
    c.value++;
    console.log('A...' + c.value);
}
var b = B();
b();
add();
add();
b();
add();
b();
b();
执行结果
A...1
add...1
add...2
A...2
add...3
A...3
A...4
[Finished in 0.4s]

总结

闭包:是一个函数,能够在函数生成时捕获附近的“值”
如上4个结论

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值