详解JavaScript闭包

简介:   要想完全明白JavaScript的闭包,要先明白js中的一些基础原理,然后我再给出一些例子来讲解闭包。   在执行JavaScript时会创建一个执行环境(excution context),执行环境定义了变量或函数可以访问的其他数据。

  要想完全明白JavaScript的闭包,要先明白js中的一些基础原理,然后我再给出一些例子来讲解闭包。

  在执行JavaScript时会创建一个执行环境(excution context),执行环境定义了变量或函数可以访问的其他数据。每个执行环境都有一个与之关联的变量对象(variable object 有些地方叫域对象(Scope object)),在执行环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。

  全局执行环境是最外层的一个执行环境。根据js实现的宿主环境的不同,环境对象不一样。浏览器中,全局执行环境是window,node.js的全局变量是global,所有的全局变量和方法都保存在全局对象中。

  每个函数都有自己的执行环境。当调用进入一个函数时,函数的执行环境就会被创建。代码在执行环境中运行时,他创建用于保存变量对象的作用域链(scope chain)。他的作用是保存一个执行环境所有可以访问的变量或函数的有序集合。作用域的最前面是当前执行的代码所在执行环境的变量对象。如果当前的执行环境是一个函数,就将函数的活动对象作为变量对象,刚开始时只有一个变量arguments。作用域链中的下一个变量对象是包含当前环境变量的外部环境也就是他的调用者,再下一个是更外层的,至到全局执行环境。

  所以在一个执行中的方法内访问一个不存在于这个执行环境中的变量时不会报错,解析器会从作用域链的顶端的变量对象开始找,如果找不到就找下一个执行环境的变量对象,一直到全局环境变量。如果有则停止查找。如果找到全局变量对象还是没有发现,就会报错。

  简单说就是,一个函数体内就是一个执行环境,当一个函数在执行时,会创建一个作用链,这个链中有自己的变量对象,同时也有外层的变量对象。

  示例1:全局执行环境

var value1 =  1;
var value2 = 2;

 

  直接运行上面的代码,也就是说我们在一个全局执行环境中定义了两个变量,所以他俩会被保存在全局对象中这里用global保存。如下图所示

 

 

示例2.全局环境中的方法

var value1 = 11;
var value2 = 22;

function log() {
    var logValue = "writing... ";
    console.log(logValue, "value1 :", value1, "  value2 ", value2);
}
log();

 

  在log函数中,他的作用域链包含两个对象:一个是自己的的变量对象(包含arguments对象)和全局环境变量对象,所以在函数内访问value1和value2时就可以沿着作用域链找找他俩。

 

 示例3:闭包(嵌套函数 )

var value1 = 11;
var value2 = 22;

function log() {
    var logValue = "writing... ";
    function nested() {
        console.log(logValue, "value1 :", value1, "  value2 ", value2);
    }
    return nested;
}
var fun1 = log();

fun1();

 

  当你在一个函数内又创建了函数,那么就会创建闭包。当函数开始执行时闭包就会在堆上分配堆栈帧,而且在函数返回时不会被释放掉。在上面的代码中有三个执行环境一个是全局执行环境,一个是log()的局部执行环境,还有一个是nested()的执行环境。nested()可以访问log和global的变量.log()可以访问自己的global的变量:

  nested()函数从log()方法中被返回,他的作用域链被初始化为log()中定义的所有活动对象,和全局变量对象,这样nested()函数就可以访问所有的变量了。更重要的是log()执行完毕后,他的变量对象不会被销毁,因为nested()函数仍然在引用这个变量对象。也可以说,log()函数执行完后,log()的作用域链被销毁,但变量对象仍然保留在内存中,直到nested()销毁后,引用的log()的变量对象才会被销毁。

 

   有c++或c经验的程序员,可能会认为返回的是一个方法的指针,nested和fun1变量是两个指向这个方法的指针,其实不然,c++言语指向方法的指针和JavaScript中对一个方法的引用有很大的不同,JavaScript中你可以认为一个方法的引用变量有一个指向方法的指针,同时也有一个隐藏的指针指向闭包。我就不再举其他的例子了,能简单明了的让大家理解闭包的原理就够了。

 

目录
相关文章
|
4月前
|
JavaScript 前端开发
JavaScript 闭包:让你更深入了解函数和作用域
JavaScript 闭包:让你更深入了解函数和作用域
|
20天前
|
JavaScript 前端开发 安全
详细讲解JavaScript中的闭包问题附代码演示
闭包是JavaScript中一关键概念,它允许内部函数访问外部函数的作用域,从而实现变量的封装与持久化。本文通过示例解释了闭包的工作原理及其优势,如数据隐藏和私有变量的实现;同时也指出了闭包可能导致的内存占用问题,强调合理使用的重要性。
19 1
|
20天前
|
JavaScript 前端开发 Java
JavaScript中的闭包概念讲解
闭包是指函数内部嵌套另一个函数,并且内部函数引用了外部函数的数据(如变量或函数)。这样的内部函数被称为闭包。以示例代码为例,`fn1` 中有两个闭包函数 `fn2` 和 `fn3`,它们都可以访问并修改 `fn1` 中的变量 `a`。
11 1
|
8天前
|
自然语言处理 前端开发 JavaScript
前端进阶必读:JS闭包深度解析,掌握这一特性,你的代码将焕然一新!
【8月更文挑战第23天】闭包是JavaScript的一项高级功能,让函数能够访问和操作外部函数作用域中的变量。本文深入解析闭包概念、组成及应用场景。闭包由函数及其词法环境构成,通过在一个函数内定义另一个函数来创建。它有助于封装私有变量、维持状态和动态生成函数。然而,不当使用闭包可能导致内存泄漏或性能问题。掌握闭包对于实现模块化代码和成为优秀前端开发者至关重要。
21 0
|
19天前
|
JavaScript 前端开发 安全
JS 闭包(1)
JS 闭包(1)
23 0
|
19天前
|
JavaScript Java
JS 闭包
JS 闭包
20 0
|
19天前
|
存储 JavaScript 前端开发
JavaScript——对闭包的看法,为什么要用闭包?说一下闭包原理以及应用场景
JavaScript——对闭包的看法,为什么要用闭包?说一下闭包原理以及应用场景
25 0
|
3月前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包是函数访问外部作用域变量的能力体现,它用于封装私有变量、持久化状态、避免全局污染和处理异步操作。
【6月更文挑战第25天】JavaScript闭包是函数访问外部作用域变量的能力体现,它用于封装私有变量、持久化状态、避免全局污染和处理异步操作。闭包基于作用域链和垃圾回收机制,允许函数记住其定义时的环境。例如,`createCounter`函数返回的内部函数能访问并更新`count`,每次调用`counter()`计数器递增,展示了闭包维持状态的特性。
41 5
|
3月前
|
设计模式 自然语言处理 JavaScript
JavaScript进阶-函数表达式与闭包
【6月更文挑战第18天】JavaScript函数不仅是代码块,还是值,具备函数表达式和闭包等特性。函数表达式如匿名函数,可赋值、传参,但不提升,过度使用影响可读性。闭包允许访问外部作用域,即使父函数已结束,但不当使用可能导致内存泄漏。理解并妥善处理这些问题,如命名函数表达式、及时释放引用,能提升代码质量。通过实践深化对这些关键概念的理解至关重要。
27 2
|
2月前
|
存储 缓存 JavaScript
js 【详解】闭包
js 【详解】闭包
25 0