JavaScript闭包学习笔记

时间:2022-07-10 22:45:46

–学习《你不知道的JavaScript》上卷笔记

function foo(){
    var a=2;
    funtion bar(){
        console.log(a);
    }
    return bar();
}
var baz=foo();
baz();//2

因为函数也是一种对象,可以被当作参数传递或作为返回值。在这个例子中,在foo()执行后,它的返回值(也就是内部的bar()函数)赋值给baz并调用baz(),可以成功访问a。

在foo()执行后,通常foo()的整个内部作用域都被销毁,但闭包可以阻止这件事情的发生,事实上foo()的内部作用于仍然存在。原因是拜bar()所生命的位置所赐,它拥有涵盖foo()内部作用域的闭包,使得该作用域能够一直存活,以供bar()在之后任何时间进行引用。bar()依然持有对该作用域的引用,这个引用就叫做闭包。因此在几微秒后变量baz被调用时,它可以访问定义时的词法作用域,也就可以访问变量a。

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

function foo(){
    var a=2;
    function baz(){
        console.log(a);
        }
    bar(baz());
}
function bar(fn){
    fn();
}
foo();//2 这就是闭包

把内部函数baz传递给bar,当调用这个内部函数的时候(现在叫做fn),它涵盖的foo()内部作用域的闭包就可以观察到了,因为它可以访问a。

无论通过何种手段将内部函数传递到所在的词法作用域以外,他都会持有对原始定义时的作用域的引用,无论在何处执行这个函数都会使用闭包。

*

回调函数中的闭包

*

function wait(message){
    setTimeout(function timer(){
        console.log(message);
        },1000);
    }
wait(hello);

将一个名为timer的内部函数传递给setTimeout(),timer具有涵盖wait()作用域的闭包,因此可以引用message。在wait()执行1000毫秒之后,他的内部作用于并不会消失,timer函数依旧保持着wait()作用域的闭包。

闭包的作用

1. 循环和闭包

for(var i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i); },i*1000);
    }

我们对这段代码预期的结果是分别输出数字1,2,3,4,5,每秒一次,一次一个。可实际的结果是,它会一每秒一次的频率输出五次6。原因是通过这种形式定义的i变量与定义在全局变量中并没有什么区别。尽管循环中的五个函数是在各个迭代中分别定义的,但是他们都被封闭在一个共享的全局作用域中,实际上只有一个i。所以我们需要在循环过程的每个迭代都需要一个闭包作用域。

将上述代码修改如下:

for(var i=1;i<=5;i++){
    (function(i){
        setTimeout(function timer(){
            console.log(i);
            },i*1000);
        }(i);
    }

在迭代中使用IIFE(立即执行函数表达)会为每一次迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代都会含有一个具有正确值的变量供我们访问。

2.模块

function coolMoudle(){
    var something="cool";
    var another=[1,2,3];
    function doSomthing(){
        console.log(somthing);
        }
    function doAnother(){
        console.log(another.join("!"));
        }
    return{
        doSomthing:doSomthing,
        doAnother:another
        };
    }
    var foo=coolMoudle();
    foo.doSomething();//cool
    foo.doAnother();//1!2!3!

doSomthing()和doAnother()函数具有涵盖模块实例内部作用域的闭包(通过调用cooMoudle()实现)。当返回一个含有属性引用的对象的方式来将函数传递到词法作用域外部时,我们已经创造了可以观察和实践闭包的条件。