详解JavaScript闭包

简介:

 要想完全明白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中你可以认为一个方法的引用变量有一个指向方法的指针,同时也有一个隐藏的指针指向闭包。我就不再举其他的例子了,能简单明了的让大家理解闭包的原理就够了。

 本文转自lpxxn博客园博客,原文链接:http://www.cnblogs.com/li-peng/p/6444317.html,如需转载请自行联系原作者


相关文章
|
3月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
2月前
|
JavaScript 前端开发
js 闭包的优点和缺点
【10月更文挑战第27天】JavaScript闭包是一把双刃剑,在合理使用的情况下,它可以带来很多好处,如实现数据封装、记忆功能和模块化等;但如果不注意其缺点,如内存泄漏、变量共享和性能开销等问题,可能会导致代码出现难以调试的错误和性能问题。因此,在使用闭包时,需要谨慎权衡其优缺点,根据具体的应用场景合理地运用闭包。
128 58
|
2月前
|
缓存 JavaScript 前端开发
js 闭包
【10月更文挑战第27天】JavaScript闭包是一种强大的特性,它可以用于实现数据隐藏、记忆和缓存等功能,但在使用时也需要注意内存泄漏和变量共享等问题,以确保代码的质量和性能。
46 7
|
2月前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包:解锁编程潜能,释放你的创造力
【10月更文挑战第25天】本文深入探讨了JavaScript中的闭包,包括其基本概念、创建方法和实践应用。闭包允许函数访问其定义时的作用域链,常用于数据封装、函数柯里化和模块化编程。文章还提供了闭包的最佳实践,帮助读者更好地理解和使用这一强大特性。
28 2
|
2月前
|
存储 缓存 自然语言处理
掌握JavaScript闭包,提升代码质量与性能
掌握JavaScript闭包,提升代码质量与性能
|
2月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包(Closures)
深入理解JavaScript中的闭包(Closures)
|
2月前
|
存储 自然语言处理 JavaScript
深入理解JavaScript的闭包(Closures)
深入理解JavaScript的闭包(Closures)
41 0
|
3月前
|
设计模式 JavaScript 前端开发
探索JavaScript中的闭包:从基础概念到实际应用
在本文中,我们将深入探讨JavaScript中的一个重要概念——闭包。闭包是一种强大的编程工具,它允许函数记住并访问其所在作用域的变量,即使该函数在其作用域之外被调用。通过详细解析闭包的定义、创建方法以及实际应用场景,本文旨在帮助读者不仅理解闭包的理论概念,还能在实际开发中灵活运用这一技巧。
|
3月前
|
缓存 JavaScript 前端开发
深入了解JavaScript的闭包:概念与应用
【10月更文挑战第8天】深入了解JavaScript的闭包:概念与应用
|
3月前
|
自然语言处理 JavaScript 前端开发
Javascript中的闭包encloure
【10月更文挑战第1天】闭包是 JavaScript 中一种重要的概念,指函数能够访问其定义时的作用域内的变量,即使该函数在其词法作用域之外执行。闭包由函数及其词法环境组成。作用域链和词法作用域是闭包的核心原理。闭包常用于数据隐藏和封装,如模块模式;在异步操作中也广泛应用,如定时器和事件处理。然而,闭包也可能导致内存泄漏和变量共享问题,需谨慎使用。