资讯专栏INFORMATION COLUMN

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

guqiu / 2182人阅读

摘要:吐槽一下,闭包这个词的翻译真是有很大的误解性啊要说闭包,要先说下词法作用域。闭包两个作用通过闭包,在外部环境访问内部环境的变量。闭包使得函数可以继续访问定义时的词法作用域。

闭包是真的让人头晕啊,看了很久还是觉得很模糊。只能把目前自己的一些理解先写下来,这其中必定包含着一些错误,待日后有更深刻的理解时再作更改。

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

要说闭包,要先说下词法作用域。

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

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

闭包 关于闭包
闭包的各个理解:
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。
作者: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,一共五次。

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

我们可以通过IIFE创建作用域。(IIFE会通过声明并立即执行一个函数来创建作用域)。

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。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95871.html

相关文章

  • 你应该要知道作用域和闭包

    摘要:写在前面对于一个前端开发者,应该没有不知道作用域的。欺骗词法作用域有两个机制可以欺骗词法作用域和。关于你不知道的的第一部分作用域和闭包已经结束了,但是,更新不会就此止住未完待续 这是《你不知道的JavaScript》的第一部分。 本系列持续更新中,Github 地址请查阅这里。 写在前面 对于一个前端开发者,应该没有不知道作用域的。它是一个既简单有复杂的概念,简单到每行代码都有它的影子...

    JouyPub 评论0 收藏0
  • 重读你不知道JS () 第一节五章

    摘要:词法作用域的查找规则是闭包的一部分。因此的确同闭包息息相关,即使本身并不会真的使用闭包。而上面的创建一个闭包,本质上这是将一个块转换成一个可以被关闭的作用域。结合块级作用域与闭包模块这个模式在中被称为模块。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 Jav...

    worldligang 评论0 收藏0
  • 你不知道 JavaScript 笔记——作用域和闭包

    摘要:理解作用域在引擎看来是两个完全不同的声明。在循环中使用闭包闭包是函数和声明该函数的词法环境的组合。回到我们上面说的在自己定义的作用域以外的地方执行,这里声明的是全局变量,使用全局变量不构成闭包。 第一章:作用域是什么 程序中变量存储在哪里,需要是怎么找到它,这就需要设计一套存储以及能方便的找到它的规则,这个规则就是作用域 编译原理 JavaScript 是一门编译语言,它与传统编译语言...

    BearyChat 评论0 收藏0
  • 重读你不知道JS () 第一节二章

    摘要:词法作用域定义在词法阶段的作用域由你在写代码时将变量和块作用域写在哪来决定的,因此当词法分析器处理代码时会保持作用域不变。欺骗词法作用域在词法分析器处理过后依然可以修改作用域。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 JavaScript 开发者,如果没...

    baihe 评论0 收藏0
  • 十分钟快速了解《你不知道 JavaScript》(卷)

    摘要:最近刚刚看完了你不知道的上卷,对有了更进一步的了解。你不知道的上卷由两部分组成,第一部分是作用域和闭包,第二部分是和对象原型。附录词法这一章并没有说明机制,只是介绍了中的箭头函数引入的行为词法。第章混合对象类类理论类的机制类的继承混入。 最近刚刚看完了《你不知道的 JavaScript》上卷,对 JavaScript 有了更进一步的了解。 《你不知道的 JavaScript》上卷由两部...

    赵春朋 评论0 收藏0

发表评论

0条评论

guqiu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<