资讯专栏INFORMATION COLUMN

云天视角-浅谈闭包

nanfeiyan / 1748人阅读

摘要:函数在执行的时候执行函数,将当前的变量对象由于当前的环境是函数,所以将其活动对象作为变量对象添加到作用域链的前端。此时,由于在执行,而作用域链也存在,所以可以在作用域链上进行查找,去访问的变量。

一、现状

闭包是jser绕不过的坎,一直在都在说,套用 simpson 的话来说:JavaScript中闭包无处不在,你只需要能够识别并拥抱它。

闭包是基于词法作用域书写代码时的自然结果,你甚至不需要为了利用它们而有意识的去创建闭包。闭包的创建和使用在你的代码中随处可见。你缺少的只是根据你的意愿来识别、拥抱和影响闭包的思维环境

二、什么是闭包(closure)
当函数可以记住并访问所在的词法作用域时,就产生了闭包。即使函数是在当前词法作用域之外执行             --《你不知道的js》(上卷)
闭包是指有权访问另一个函数作用域中的变量的函数 --《JavaScript高级程序设计》

先来看一个例子:
例子1:

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

foo()

这是闭包吗?
这个代码从技术上来说是,但也可以说不是。准确的来说bar()对a的引用的方法是词法作用域的查找规则。我们再来看:
例子2:

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

在例2中,我们将bar()函数本身当做一个值类型进行传递,函数bar()能够访问foo()的内部作用域。在这个例子中,它在自己定义的词法作用域以外的地方执行。

三、怎么形成的

要了解清楚,得先了解几个概念

作用域链(scope chain)

词法作用域

词法作用域

每个函数都有自己的执行环境。这个环境可以访问外部环境,以此类推。每个环境能访问到的标识符集合,称之为 作用域,也就是词法作用域

作用域链(scope chain)

将作用域一层一层的嵌套,就形成了作用域链

如下,通常我们都希望foo()在执行完成以后,整个的内部作用域都被销毁。因为我们知道引擎有垃圾回收机制用来释放不再使用的内存空间。由于看上去foo()的内容不会再被使用,所以很自然的想到会对其回收。但是,事实上内部作用域依然存在

var globalVar = 10;
function foo() {
    var fooVar = 20;
    function bar() {
        var barVar = 30;
        return globalVar + fooVar + barVar;
    }
    return bar;
}
var baz = foo();
baz();

如上,用一张图表示

这个作用域链在函数创建的时候就保存起来了。

baz()函数在执行的时候(执行bar()函数),将当前的变量对象(由于当前的环境是函数,所以将其活动对象作为变量对象)添加到作用域链的前端。此时,由于bar()在执行,而作用域链也存在,所以可以在作用域链上进行查找,去访问foo()的变量。

四、闭包的应用场景有哪些

创建私有变量或函数

五、闭包的缺点

闭包中的值是存在于内存中,滥用的话会导致内存消耗过大

闭包经典问题

// 函数作用:希望它返回一个数组。该数组的元素为遍历的索引值
function hello(){
    var res = [];
    for (var i = 0,len = 5;i < len;i++){
        res[i] = function () {
            return i;
        }
    }
    return res;
}

返回的结果跟我们期待的不一样,因为:闭包保存的是整个变量对象,而不是每个变量。
解决方案:

function hello(){
    var res = [];
    for (var i = 0,len = 5;i < len;i++){
        res[i] = (function(i){
            return i;
        })(i)
    }
    return res;
} 

这里,没有没有把闭包直接赋值给数组。而是定义了一个匿名函数,并且将立即执行该匿名函数的结果赋值给数组,由于参数是按值传递的,所以会将当前值传给参数num。

参考资料:《你不知道的js》(中卷)、《JavaScript高级程序设计》

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

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

相关文章

  • 2017文章总结

    摘要:欢迎来我的个人站点性能优化其他优化浏览器关键渲染路径开启性能优化之旅高性能滚动及页面渲染优化理论写法对压缩率的影响唯快不破应用的个优化步骤进阶鹅厂大神用直出实现网页瞬开缓存网页性能管理详解写给后端程序员的缓存原理介绍年底补课缓存机制优化动 欢迎来我的个人站点 性能优化 其他 优化浏览器关键渲染路径 - 开启性能优化之旅 高性能滚动 scroll 及页面渲染优化 理论 | HTML写法...

    dailybird 评论0 收藏0
  • 2017文章总结

    摘要:欢迎来我的个人站点性能优化其他优化浏览器关键渲染路径开启性能优化之旅高性能滚动及页面渲染优化理论写法对压缩率的影响唯快不破应用的个优化步骤进阶鹅厂大神用直出实现网页瞬开缓存网页性能管理详解写给后端程序员的缓存原理介绍年底补课缓存机制优化动 欢迎来我的个人站点 性能优化 其他 优化浏览器关键渲染路径 - 开启性能优化之旅 高性能滚动 scroll 及页面渲染优化 理论 | HTML写法...

    hellowoody 评论0 收藏0
  • 2017文章总结

    摘要:欢迎来我的个人站点性能优化其他优化浏览器关键渲染路径开启性能优化之旅高性能滚动及页面渲染优化理论写法对压缩率的影响唯快不破应用的个优化步骤进阶鹅厂大神用直出实现网页瞬开缓存网页性能管理详解写给后端程序员的缓存原理介绍年底补课缓存机制优化动 欢迎来我的个人站点 性能优化 其他 优化浏览器关键渲染路径 - 开启性能优化之旅 高性能滚动 scroll 及页面渲染优化 理论 | HTML写法...

    wwolf 评论0 收藏0
  • 浅谈JavaScript中的闭包

    摘要:在内部,理所当然能访问到局部变量,但当作为的返回值赋给外的全局变量时,神奇的事情发生了在全局作用域中访问到了,这就是闭包。而闭包最神奇的地方就是能在一个函数外访问函数中的局部变量,把这些变量用闭包的形式放在函数中便能避免污染。 一、闭包是什么? 《JavaScript高级程序设计》中写道:闭包是指有权访问另一个函数作用域中的变量的函数,如果用下定义的观点看,这句话就是说闭包是函数,我...

    Riddler 评论0 收藏0
  • 浅谈JavaScript闭包

    摘要:但是函数返回了内部函数,内部函数会随时访问变量所以垃圾回收机制是不会回收函数的内部作用域的,这就是闭包的含义。也就是函数在定义的词法作用域以外的地方被调用,闭包使得函数可以继续访问定义时的词法作用域。   初学JavaScript闭包时,闭包这个概念在我眼里及其的神秘,也不知道这个东西在讲什么,尤其某些地方的闭包概念定义的非常抽象,属于那种本来你可能明白这个概念,看了反而又把你给绕糊涂...

    hsluoyz 评论0 收藏0

发表评论

0条评论

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