[JS]《你不知道的Javascript·上》——词法做用域和闭包

闭包是真的让人头晕啊,看了好久仍是以为很模糊。只能把目前本身的一些理解先写下来,这其中一定包含着一些错误,待往后有更深入的理解时再做更改。闭包

吐槽一下,闭包这个词的翻译真是有很大的误解性啊……函数

要说闭包,要先说下词法做用域。翻译

词法做用域

简单来讲,词法做用域就是定义在词法阶段的做用域。换句话说,词法做用域是由你在写代码时将变量和块做用域写在哪里来决定的。

不管函数在哪里被调用,也不管它如何被调用,它的词法做用域都只由函数被声明时所处的位置决定。code

闭包

关于闭包

闭包的各个理解:
当函数能够记住并访问所在的词法做用域,即便函数是在当前词法做用域以外执行,这时就产生了闭包。
做者:Cat Chen
闭包就是由函数创造的一个词法做用域,里面建立的变量被引用后,能够在这个词法环境以外自由使用。
闭包一般用来建立内部变量,使得这些变量不能被外部随意修改,同时又能够经过指定的函数接口来操做。
function foo(){
    var a=2;
    function bar(){
        console.log(a);
    }
    return bar;
}
var baz=foo();
baz();//2——闭包

两个做用:
(1):经过闭包,在外部环境访问内部环境的变量。
(2):使得这些变量一直保存在内存中,不会被垃圾回收。接口

上面的代码例子中,函数在定义时的词法做用域之外的地方被调用。闭包使得函数能够继续访问定义时的词法做用域。//?内存

调用的方式不单单只有以上代码中的经过不一样的标识符引用调用其内部的函数,作用域

可是无论经过什么手段将内部函数传递到所在的词法做用域之外,它都会持有对原始定义做用域的引用,不管在何处执行这个函数都会使用闭包。

循环和闭包

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

正常预想下,上面这段代码咱们觉得是分别输出数字1-5,每秒一个。
但实际上,运行时输出的倒是每秒输出一个6,一共五次。io

缘由是,延迟函数的回调会在循环结束时才执行。
根据做用域的工做原理,循环中的五个函数是在各个迭代中分别定义的,可是它们都被封闭在一个共享的全局做用域中,实际上只有一个i。console

咱们能够经过IIFE建立做用域。(IIFE会经过声明并当即执行一个函数来建立做用域)。function

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

可是这样建立的做用域里是空的,须要有本身的变量:

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

改进获得:

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

ES6引入的let在循环中不止会被声明一次,在每次迭代都会声明:

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

应用场景:模块

模块也是利用了闭包的一个强大的代码模式。

function CoolModule(){
    var something="cool";
    var anothor=[1,2,3];
    
    function doSomething(){
        console.log(something);
    }
    
    function doAnthor(){
        console.log(anothor.join("!"));
    }
    
    return{
        doSomethig:doSomething,
        doAnothor:doAnother
    };
}

var foo=CoolMOdule();
foo.doSomething();//cool
foo.doAnother();//1!2!3
模块有2个主要特征:
(1):为建立内部做用域而调用了一个包装函数;
(2):包装函数的返回值必须至少包括一个对内部函数的引用,这样就会建立涵盖整个包装函数内部做用域的闭包。

关于模块的引入

import能够将一个模块中的一个或多个API导入到当前做用域中,并分别绑定在一个变量上; module会将整个模块的API导入并绑定到一个变量上; export会将当前模块的一个标识符导出为公共API。
相关文章
相关标签/搜索