资讯专栏INFORMATION COLUMN

简单理解JavaScript中的闭包

sihai / 2629人阅读

摘要:闭包在我理解是一种比较抽象的东西。所以我写了一篇博文来方便自己理解闭包。那么现在我们可以解释一下闭包的第一个定义在计算机科学中,闭包是引用了自由变量的函数。循环中创建闭包在我们使用的关键字之前,闭包的一个常见问题就出现在循环中创建闭包。

零. 前言

从我开始接触前端时就听说过闭包,但是一直不理解闭包究竟是什么。上网看了各种博客,大家对闭包的说法不一。闭包在我理解是一种比较抽象的东西。所以我写了一篇博文来方便自己理解闭包。博主是第一次写博文,如果在文章中有什么看不懂或者概念错误的地方,请大家多多见谅和指出错误。

一. 闭包的定义

再说闭包之前,首先让我们先来理解一下自由变量和约束变量。

在程序设计语言中,变量可以分为自由变量约束变量两种。简单来说,一个函数里局部变量和参数都被认为是约束变量;而不是约束变量的则是自由变量。下面我们通过一个demo来解说。

var x = 10; // 相对于fn来说,x是一个自由变量
function fn(){
    var b = 20;
    console.log( x + b ); // 30 
}
fn();

在上述例子中,相对于函数实例fn而言,x是一个自由变量,因为x并不是fn的局部变量和参数。而b是fn的局部参数,所以b是fn的约束变量。

那么现在我们可以解释一下闭包的第一个定义:

在计算机科学中,闭包是引用了自由变量的函数。

其实闭包不一定要是函数实例,也可以是代码块,只要满足可以保存变量在内存,同时有一些方法对于这些变量进行访问就行了。

所以,我们可以引申出闭包的第二个定义:

闭包是由函数和与其相关的引用环境组合而成的实例,环境由闭包创建时在作用域中的任何局部变量和参数组成。

闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

// 例子1
function Person(){
    var name,
        age;

    function init(name, age){
        name = name;
        age = age;
    }
    function show(){
        console.log("name: %s, age: %d", name, age);
    }

    return {
        init: init,
        show: show
    }
}

var eyesiM = Person.init("EyesiM", 22); // 闭包的实例1
var dcc = Person.init("Dcc", 20); // 闭包的实例2

eyesiM.show(); // name: EyesiM, age: 22
dcc.show(); // name: Dcc, age: 20

上面的变量eyesiM和变量dcc就是闭包的实例,其中变量eyesiM的环境中局部变量name和age的值为"EyesiM"和22;变量dcc的环境中的局部变量name和age的值为"Dcc"和20。

二. 闭包的应用

闭包可以用来在一个函数与一组“私有”变量之间建立关联关系。在给定函数被多次调用的过程中,这些私有变量能够保存在内存中。变量的作用域仅限于包含它们的函数,因此无法从除包含它们的函数之外进行访问。

1. 模块模式

在Java等等一些语言里面会有private关键字来将方法和变量声明为私有的,即它们只能被同一个类中的其它方法所调用。

JavaScript不提供原生的支持,但是可以使用闭包模拟私有变量和私有方法。私有变量可以限制对代码的访问;避免非核心的方法弄乱了代码的公共接口部分。

// 例子2
var demo = (function(){
    // 模拟私有变量
    var count = 0;

    function show(){
        console.log("count: %d", count++);
    }

    return {
        show: show
    }
})();

demo.show(); // 0
demo.count; // 我们不能直接引用,所以这里会返回undefined

在我理解,模块模式可以有两种用途:

立即调用函数表达式(IIFE):将我们自身的变量和方法封装起来,可以避免全局污染。例子2就是一个IIFE的例子。

引入依赖:我们可以引入对某一个全局对象的依赖,对这一个全局进行扩充。下面我们可以通过一个例子来表示。

// MODULE 就是一个全局对象,如果不存在就初始化为`{}`,我们使用局部参数my指向这个对象,接着我们给这个全局对象添加属性`method`,然后返回指向这个全局对象的引用。
var MODULE = (function (my) {
    my.method = {
        // add code
    }
    return my;
}(MODULE || {}));
2. 循环中创建闭包

在我们使用ES6的let关键字之前,闭包的一个常见问题就出现在循环中创建闭包。

// 例子3



    
    Document


    
apple
banana
orange
function showColor(item) {
    console.log("id: %s, color: %s", item.id, item.color);
}

function addHTML() {
    var colors = [{
        id: "apple",
        color: "red"
    }, {
        id: "banana",
        color: "yellow"
    }, {
        id: "orange",
        color: "orange"
    }];

    for (var i = 0, length = colors.length; i < length; i++) {
        var item = colors[i];

        // error
        // 当我们把鼠标依次移过id为"apple", "banana", "orange"的div时,控制台打印出的是
        // id: orange, color: orange
        // id: orange, color: orange
        // id: orange, color: orange
        // document.getElementById(item.id).onmouseover = function(){
        //    showColor(item);
        // }

        // success
        // 当我们把鼠标依次移过id为"apple", "banana", "orange"的div时,控制台打印出的是
        // id: apple, color: red
        // id: banana, color: yellow
        // id: orange, color: orange
        document.getElementById(item.id).onmouseover = function(item) {
            return function() {
                showColor(item);
            };
        }(item);
    }
}
addHTML();
三. 闭包的注意点

闭包避免了环境中的变量被当成垃圾回收,因此使用闭包会使得闭包中的变量都被保存在内存中。

在一般的多页面中,我们关闭或重定向了页面之后,浏览器会自动回收原页面所占用的资源,但是如果我们所做的项目是SPA的话,就需要考虑到内存的使用,所以一定要慎用闭包。

四. 参考资料

yangfch3的笔记-闭包

深入理解JavaScript 模块模式

详解javascript立即执行函数表达式(IIFE)

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

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

相关文章

  • 通过示例学习JavaScript闭包

    摘要:译者按在上一篇博客,我们通过实现一个计数器,了解了如何使用闭包,这篇博客将提供一些代码示例,帮助大家理解闭包。然而,如果通过代码示例去理解闭包,则简单很多。不过,将闭包简单地看做局部变量,理解起来会更加简单。 - 译者按: 在上一篇博客,我们通过实现一个计数器,了解了如何使用闭包(Closure),这篇博客将提供一些代码示例,帮助大家理解闭包。 原文: JavaScript Clos...

    xingpingz 评论0 收藏0
  • JavaScript闭包简单理解

    摘要:闭包是函数内部的子函数能读取局部变量二闭包的特点函数里面嵌套函数内部函数能访问外部函数的变量定义的参数和变量不会回收三闭包的前提先明白什么是全局变量和局部变量中声明变量格式关键字变量名标识符。建议在退出函数之前,将不使用的局部变量全部删除。 一、闭包的概念 闭包是指一个函数能够访问其函数外部作用域中的变量。JavaScript闭包是函数内部的子函数能读取局部变量 二、闭包的特点 函数...

    hzx 评论0 收藏0
  • 还担心面试官问闭包

    摘要:一言以蔽之,闭包,你就得掌握。当函数记住并访问所在的词法作用域,闭包就产生了。所以闭包才会得以实现。从技术上讲,这就是闭包。执行后,他的内部作用域并不会消失,函数依然保持有作用域的闭包。 网上总结闭包的文章已经烂大街了,不敢说笔者这篇文章多么多么xxx,只是个人理解总结。各位看官瞅瞅就好,大神还希望多多指正。此篇文章总结与《JavaScript忍者秘籍》 《你不知道的JavaScri...

    tinyq 评论0 收藏0
  • 【进阶2-2期】JavaScript深入之从作用域链理解闭包

    摘要:使用上一篇文章的例子来说明下自由变量进阶期深入浅出图解作用域链和闭包访问外部的今天是今天是其中既不是参数,也不是局部变量,所以是自由变量。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第7天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计...

    simpleapples 评论0 收藏0
  • JavaScript闭包初探

    摘要:说了半天,究竟什么是闭包呢闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。弹出上面函数中的函数就是闭包,就是通过建立函数来访问函数内部的局部变量。闭包会在父函数外部,改变父函数内部变量的值。 JavaScript的闭包 首先声明,这是一篇面向小白的博客,不过也欢迎各位大牛批评指正,谢谢。   其实关于闭包各个论坛社区里都有很多的文章来讲它,毕竟闭包是JavaScri...

    沈建明 评论0 收藏0

发表评论

0条评论

sihai

|高级讲师

TA的文章

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