猿记录

一个记录、分享的博客

您的位置:主页 > 技术专栏 >

JavaScript 学习笔记 之 作用域 - 闭包

2018-09-02 11:00:20 作者:yxl 次阅读 技术专栏

5.1 什么是闭包

当函数可以记住并访问定义时所在的(上下文)词法作用域时,就产生了闭包,即使函数是在定义时的词法作用域之外执行

//全局作用域
function foo() {//作用域1	
    var a = 2;	
    function bar() {
        console.log(a);
    }	
   return bar;
}
var fun = foo();//记住了函数定义时候的作用域1、全局作用域fun(); //2————这就是闭包

闭包发生在定义时。(只有调用了外部函数生成了内部作用域才能生成闭包,这点很重要)

当运行了var fun=foo();时,发生了闭包。

fun记住了此时作用域1、全局作用域

记住全局作用域其实意义不大,因为全局作用域只有一个,但每运行一次foo(),都会生成一个新的作用域1。

因为当运行完一次foo()时,JavaScript引擎都会销毁foo()的整个内部作用域,再次运行foo()时,又会生成一个新的内部作用域。

而发生闭包后,内部作用域并不会消失,因为fun,或者说bar()依然在使用在该内部作用域。

//全局作用域
function a(){//作用域1	
    function b(){	}	
    return b;
}
var fun1=a();
var fun2=a();
console.log(fun1===fun2);//false

第一次调用a()时生成了一个作用域,第二次调用a()时又生成了一个全新的作用域,即使没有对内部作用域进行任何修改,fun1和fun2闭包记住的作用域也完全是两个作用域。这就是闭包的效果。

//全局作用域
function a(){	
    return a;
}
var fun1=a();
var fun2=a();
console.log(fun1===fun2);//true

而在这个例子中,fun1和fun2所记住的都是全局作用域,因此fun1===fun2。

5.2 循环与闭包

为了更好的说明闭包,循环是个最常见的例子

(注:延迟函数的回调会做循环结束时才执行,即使执行的是setTimeout(...,0) ,但是定义发生在循坏过程中,因此定义时记下的作用域和执行时的作用域完全不同。)

这个函数在定义的词法作用域以外的地方被引用。闭包使得函数可以继续访问定义时的词法作用域。

//全局作用域
for(let i = 1; i <= 5; i++) {//作用域2	
(function() {//作用域1		
    setTimeout(function timer() {			
    console.log(i);		}, 0);	
    })
 ();
}

由于使用的是let进行定义,因此i作为for循环的块作用域中的变量

for循环每个迭代中,定义的timer函数都记录下了有着不同的i值的作用域2

因此在定义的词法作用域以外的地方被引用时,输出的是不同的作用域2中的i值(1、2、3、4、5)。

//全局作用域for(var i = 1; i <= 5; i++) {	
    (function() {//作用域1		
        setTimeout(function timer() {			
            console.log(i);		}, 0);	
        })
    ();
}

而如果使用var 进行定义,事实上i会被绑定到全局作用域

for循环的迭代中,定义的timer函数记录的是相同全局作用域中的i

因此在定义的词法作用域以外的地方被调用时,输出的是相同的全局作用域中的i(此时i循环后值为6),因此输出结果是5个6。


凡本站注明“本站”或“投稿”的所有文章,版权均属于本站或投稿人,未经本站授权不得转载、摘编或利用其它方式使用上述作品。

编辑:yxl 关键词:
0

网友评论