理解JavaScript闭包

来源:互联网 发布:雷洋事件最新进展知不 编辑:程序博客网 时间:2024/06/11 02:44

前言

闭包是JavaScript中比较重要的一个知识点,在开发中经常会与之打交道。本文的目的在于对闭包概念进行回顾总结,加深对其理解。

正文

闭包是什么?

在深入接触某个新事物之前,得对它的基本概念有一个初步的认识。对于闭包,我们可以将它理解为:

有权访问另一函数作用域中变量的函数。

换句话说,闭包本质上也是函数,只不过与一般的函数不同,它除了可以访问自身作用域中的变量外,还可以访问其它函数作用域中的变量。

它是怎么做到的?在彻底弄清楚这个问题之前,我们需要先理解下面两个概念。


执行环境

在JavaScript中,每一个执行环境都有与之相关联的变量对象,在执行环境中定义的所有变量、函数都作为该变量对象的属性而存在。

在Web浏览器中,全局执行环境的变量对象是window对象。

每一个函数也有自己的执行环境,同理,也有与之相关联的变量对象。在该函数内定义的所有变量与函数,都保存在该函数的变量对象中。


作用域链

当代码在某一执行环境中运行时,解析器将会构建起该执行环境的作用域链。

作用域链实质上是储存着一组指向不同变量对象指针的列表。该列表的第一个位置存储的指针指向当前执行环境所关联的变量对象,第二个位置存储的指针指向外部执行环境所关联的变量对象,以此类推,作用域链的最后一个指针指向全局变量对象。

执行环境中的代码在对某一变量进行访问时实际上会循着作用域链由前至后查找。即先从本身执行环境的变量对象开始查找,一直查至全局变量对象。

而这其实也解释了为什么在函数执行环境中可以访问全局变量,但是在全局执行环境中无法访问函数本身定义的变量。

var x=1;(() => console.log(x)) () //1
function f () {    var y = 2;}console.log(y) // y is not defined

在对上述两个概念进行回顾后,我们进入正题。

对于一般的执行环境,在其代码全都执行结束后,与之关联的变量对象会从内存中被销毁掉,理所当然的,在该环境下声明的变量与函数也将全部被销毁(全局执行环境需要在浏览器被关掉后变量对象才会被销毁)。

而闭包不一样,闭包有能力阻止这样的销毁行为,也正是因为这种能力,使得闭包可以访问另一函数作用域中的变量

来看下面一段代码

function f1 () {    var x = 1;    function f2 () {        console.log(x) //1    }    return f2}var f = f1();f() // 1

在上述代码中,f2就是闭包,我们观察一下f2函数执行环境的作用域链,可以发现一共储存着3个指针,由前至后分别指向:
1. f2执行环境关联的变量对象
2. f1执行环境关联的变量对象
3. 全局变量对象

而正因为f2作用域链中存在指向f1变量对象的指针,因此f1函数在执行结束后其变量对象不会被销毁,其中存储的变量仍可由函数f2通过作用域链来进行查找。

综上,我们可以对闭包概念进一步简化,它是:

定义在一个函数内部的函数,可以通过本身的作用域链对外部函数的变量进行访问。


闭包的副作用

  1. 闭包使得函数中的变量无法被销毁,在手动释放前变量将一直保存在内存中,对内存消耗很大,影响网页性能。
  2. 闭包保存的是外部环境的整个变量对象,因此只能取得包含函数中任何变量的最后一个值。

由于作者水平有限,倘若文章存在错误,或者你对文章内容有更好地补充,欢迎指正或提出建议,我将感激不尽。

原创粉丝点击