看了知乎上的话题 如何才能通俗易懂的解释javascript里面的‘闭包’?,受到一些启发,因此结合实例将回答中几个精要的答案做一个简单的分析以便加深理解。

1. "闭包就是跨作用域访问变量。" 

【示例一】

var name = 'wangxi'
function user () {
// var name = 'wangxi'
function getName () {
console.log(name)
}
getName()
}
user() // wangxi

在 getName 函数中获取 name,首先在 getName 函数的作用域中查找 name,未找到,进而在 user 函数的作用域中查找,同样未找到,继续向上回溯,发现在全局作用域中存在 name,因此获取 name 值并打印。这里很好理解,即变量都存在在指定的作用域中,如果在当前作用中找不到想要的变量,则通过作用域链向在父作用域中继续查找,直到找到第一个同名的变量为止(或找不到,抛出 ReferenceError 错误)。这是 js 中作用域链的概念,即子作用域可以根据作用域链访问父作用域中的变量,那如果相反呢,在父作用域想访问子作用域中的变量呢?——这就需要通过闭包来实现。

【示例二】

function user () {
var name = 'wangxi'
return function getName () {
return name
}
} var userName = user()()
console.log(userName) // wangxi

分析代码我们知道,name 是存在于 user 函数作用域内的局部变量,正常情况下,在外部作用域(这里是全局)中是无法访问到 name 变量的,但是通过闭包(返回一个包含变量的函数,这里是 getName 函数),可以实现跨作用域访问变量了(外部访问内部)。因此上面的这种说法完整的应该理解为:

闭包就是跨作用域访问变量 —— 内部作用域可以保持对外部作用域中变量的引用从而使得(更)外部作用域可以访问内部作用域中的变量。(还是不理解的话看下一条分析)

2. "闭包:在爷爷的环境中执行了爸爸,爸爸中返回了孙子,本来爸爸被执行完了,爸爸的环境应该被清除掉,但是孙子引用了爸爸的环境,导致爸爸释放不了。这一坨就是闭包。简单来讲,闭包就是一个引用了父环境的对象,并且从父环境中返回到更高层的环境中的一个对象。"

这个怎么理解呢?首先看下方代码:

【示例三】

function user () {
var name = 'wangxi'
return name
} var userName = user()
console.log(userName) // wangxi

:这是闭包吗?

:当然不是。首先要明白闭包是什么。虽然这里形式上看好像也是在全局作用域下访问了 user 函数内的局部变量 name,但是问题是,user 执行完,name 也随之被销毁了,即函数内的局部变量的生命周期仅存在于函数的声明周期内,函数被销毁,函数内的变量也自动被销毁。

但是使用闭包就相反,函数执行完,生命周期结束,但是通过闭包引用的外层作用域内的变量依然存在,并且将一直存在,直到执行闭包的的作用域被销毁,这里的局部变量才会被销毁(如果在全局环境下引用了闭包,则只有在全局环境被销毁,比如程序结束、浏览器关闭等行为时才会销毁闭包引用的作用域)。因此为了避免闭包造成的内存损耗,建议在使用闭包后手动销毁。还是上面示例二的例子,稍作修改:

【示例四】

function user () {
var name = 'wangxi'
return function getName () {
return name
}
} var userName = user()() // userName 变量中始终保持着对 name 的引用
console.log(userName) // wangxi userName = null // 销毁闭包,释放内存

【为什么 user()() 是两个括号:执行 user()  返回的是 getName 函数,要想获得 name 变量,需要对返回的 getName 函数执行一次,所以是 user()()】

根据观点2,分析一下代码:在全局作用域下创建了 userName 变量(爷爷),保存了对 user 函数最终返回结果的引用(即局部变量 name 的值),执行 user()()(爸爸),返回了 name(孙子),正常情况下,在执行了 user()() 之后,user 的环境(爸爸)应该被清除掉,但是因为返回的结果 name(孙子)引用了爸爸的环境(因为 name 本来就是存在于 user 的作用域内的),导致 user 的环境无法被释放(会造成内存损耗)。

那么【"闭包就是一个引用了父环境的对象,并且从父环境中返回到更高层的环境中的一个对象。"】如何理解?

我们换个说法:如果一个函数引用了父环境中的对象,并且在这个函数中把这个对象返回到了更高层的环境中,那么,这个函数就是闭包。

还是看上面的例子:

getName 函数中引用了 user(父)环境中的对象(变量 name),并且在函数中把 name 变量返回到了全局环境(更高层的环境)中,因此,getName 就是闭包。

3. "JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。" ——《JavaScript权威指南》

这句话对闭包中对变量的引用的理解很有帮助。我们看下面的例子:

var name = 'Schopenhauer'
function getName () {
console.log(name)
} function myName () {
var name = 'wangxi'
getName()
} myName() // Schopenhauer

如果执行 myName() 输出的结果和你想象的不一样,你就要再回去看看上面说的这句话了,

JavaScript 中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

执行 myName,函数内部执行了 getName,而 getName 是在全局环境下定义的,因此尽管在 myName 中定义了变量 name,对getName 的执行并无影响,getName 中打印的依然是全局作用域下的 name。

我们稍微改一下代码:

var name = 'Schopenhauer'

function getName () {
  var name = 'Aristotle'
var intro = function() { // 这是一个闭包
console.log('I am ' + name)
}
return intro
} function showMyName () {
var name = 'wangxi'
var myName = getName()
myName()
} showMyName() // I am Aristotle

结果和你想象的一样吗?结果留作聪明的你自己分析~

以上就是对 js 中闭包的理解,如果有误,欢迎指正。最后引用一段知乎问题下关于闭包概念的一个回答。

作者:萧潇 链接:https://www.zhihu.com/question/34547104/answer/197642727

什么是闭包?

简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。

为什么需要闭包?

局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。

特点

  • 占用更多内存
  • 不容易被释放

何时使用?

变量既想反复使用,又想避免全局污染

如何使用?

  1. 定义外层函数,封装被保护的局部变量。
  2. 定义内层函数,执行对外部函数变量的操作。
  3. 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
 

【参考】

浅谈JavaScript的闭包和作用域链

由一道题图解JavaScript的作用域

如何才能通俗易懂的解释javascript里面的‘闭包’?

如何才能通俗易懂的解释javascript里面的"闭包"?的更多相关文章

  1. 如何才能通俗易懂的解释js里面的‘闭包’?

    1. "闭包就是跨作用域访问变量." [示例一] var name = 'wangxi' function user () { // var name = 'wangxi' fun ...

  2. 如何才能通俗易懂地解释JS中的的"闭包"?

    看了知乎上的话题 如何才能通俗易懂的解释javascript里面的‘闭包’?,受到一些启发,因此结合实例将回答中几个精要的答案做一个简单的分析以便加深理解. 1. "闭包就是跨作用域访问变量 ...

  3. JavaScript 作用域和闭包——另一个角度:扩展你对作用域和闭包的认识【翻译+整理】

    原文地址 --这篇文章有点意思,可以扩展你对作用域和闭包的认识. 本文内容 背景 作用域 闭包 臭名昭著的循环问题 自调用函数(匿名函数) 其他 我认为,尝试向别人解释 JavaScript 作用域和 ...

  4. 【转】能否用讲个故事的方式,由浅入深,通俗易懂地解释一下什么是天使投资,VC,PE.

    能否用讲个故事的方式,由浅入深,通俗易懂地解释一下什么是天使投资,VC,PE 今天在知乎上看到一篇文章,觉得值得一转的,Here. 我给楼主讲个完整点的故事吧.长文慎点,前方高能,自备避雷针.18岁以 ...

  5. 从Java开发者的视角解释JavaScript

    我们无法在一篇博文里解释JavaScript的所有细节.如果你正或多或少地涉及了web应用程序开发,那么,我们的Java工具和技术范围报告揭示了,大多数(71%)Java开发者被归到了这一类,只是你对 ...

  6. 通俗易懂的解释:什么是API

    API 全称 Application Programming Interface,即应用程序编程接口. 看到这里,急性子的小白同学马上就憋不住了:这不管是英文还是中文我每个字都懂啊,只是凑一块就不知道 ...

  7. 深入理解javascript原型和闭包(16)——完结

    之前一共用15篇文章,把javascript的原型和闭包. 首先,javascript本来就“不容易学”.不是说它有多难,而是学习它的人,往往都是在学会了其他语言之后,又学javascript.有其他 ...

  8. 《深入理解javascript原型和闭包系列》 知识点整理(转)

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

  9. javascript深入理解闭包

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

随机推荐

  1. 201521123003《Java程序设计》第1周学习总结

    1. 本周学习总结 1.了解Java语言的特点: 2.明确了jdk,jre,jvm的具体含义及区别 3.掌握java的编译及运行步骤 4.了解jdk文档的用法 2. 书面作业 Q1.为什么java程序 ...

  2. Markdown格式

    一个例子: 例子开始 1. 本章学习总结 今天主要学习了三个知识点 封装 继承 多态 2. 书面作业 Q1. java HelloWorld命令中,HelloWorld这个参数是什么含义? 今天学了一 ...

  3. JQUERY选中问题

    单选,复选,下拉列表的全选选中问题 基本思路就是找到元素,操作元素,关于怎么找看jquery简介,主要学习记住具体操作用到的方法   复选框的全选以及设置选中问题:   jquery中提供prop方法 ...

  4. JSP第一篇【JSP介绍、工作原理、生命周期、语法、指令、行为】

    什么是JSP JSP全名为Java Server Pages,java服务器页面.JSP是一种基于文本的程序,其特点就是HTML和Java代码共同存在! 为什么需要JSP JSP是为了简化Servle ...

  5. 7z命令行 极限压缩指令

    摘抄自http://www.cnblogs.com/qanholas/archive/2011/10/03/2198487.html 7za a -t7z bag.7z "/home/fil ...

  6. mongodb 在windows下面进行分片

    在mongodb里面存在另一种集群,就是分片技术,跟sql server的表分区类似,我们知道当数据量达到T级别的时候,我们的磁盘,内存就吃不消了,针对这样的场景我们该如何应对. 一:分片 mongo ...

  7. Bootstrap框架的了解和使用之栅格系统

       前    言 Bootstrap Bootstrap 包含了一个响应式的.移动设备优先的.不固定的网格系统,可以随着设备或视口大小的增加而适当地扩展到 12 列.它包含了用于简单的布局选项的预定 ...

  8. 一个基于Asp.net MVC的博客类网站开源了!

    背景说明: 大学时毕业设计作品,一直闲置在硬盘了,倒想着不如开源出来,也许会对一些人有帮助呢,而且个人觉得这个网站做得还是不错了,毕竟是花了不少心思,希望对你有所帮助. github地址:https: ...

  9. Qt下 QString转char*

    Qt下面,字符串都用QString,确实给开发者提供了方便.Qt再使用第三方开源库时,由于库的类型基本上都是标准的类型,字符串遇的多的就是Char*类型 Qt再使用第三方开源库时,由于库的类型基本上都 ...

  10. form表单中enctype="multipart/form-data"的传值问题

    form表单中enctype="multipart/form-data"的传值问题!! Form表单中enctype="multipart/form-data" ...