闭包
作用域链和执行上下文

理解闭包前,先引入一个概念,作用域链

用我自己理解的讲:在一段程序中,程序内的变量、函数等都被串在这条链上,当我们使用这些变量、函数时,程序就会在这条链中搜索,如果没有找到你调用的变量或函数,就会出现问题

举个例子:

var a = 0,b = 1
function add(a, b){return a + b
}
//上面的a,b还有add就被串在了这个程序的作用域链中
console.log(add(a, b))
//调用add(a, b),程序就会在作用域链上搜索,找到add方法和a、b变量

这里我们的变量和函数都是定义在全局中(最外层)的,如果定义在函数内呢?

var a = 0
function foo(){var bar = 0console.log(a)//0
}
//在foo外层访问bar变量
console.log(bar)//报错Uncaught ReferenceError: bar is not defined

为什么在这里报错说bar没有定义呢?

按照我的理解,作用域链分为子作用域链和父作用域链,本例变量a和函数foo串在外层作用域链中,而bar串在foo函数内的作用域链中,两条链的关系就和父子一样

当我们在全局(最外层)中访问a变量(console.log(a)),程序就会优先在全局作用域链(也就是上面的父作用域链)中查找a变量

当我们在foo函数中访问bar变量(console.log(bar),程序就会优先在foo作用域链中查找bar变量

到这里一切安好,直到我们在全局中访问foo函数中的bar变量,就出了问题,bar定义在foo作用域链中,程序在父作用域链中进行搜索,并不会进入子作用域链中,自然是搜索不到的,程序就会认为我们没有定义这个变量,就会报错

但我们在foo方法中访问a变量,为什么又可以了呢?

原来程序不仅仅会在子作用域链中搜索,还会上升到父作用域链,这时候foo链中找不到,就会去全局链中查找,自然能访问到变量a

到这里我们了解了程序在作用域链上的搜索是有范围的,它的范围就称作JavaScript中的执行上下文(简称上下文)

变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为。(《JavaScript高级程序设计(第四版)》)

到这里就很明确啦,js中的函数有属于自己的作用域,在它自有的作用域中可以访问外部,但外部不能反向访问函数内部的变量

明白了作用域链和执行上下文的概念,接下来就看看闭包是什么

闭包

匿名函数经常被人误认为是闭包(closure)。闭包指的是那些引用了另一个函数作用域中变量的函数。(《JavaScript高级程序设计(第四版)》)

function foo(){var a = 0return function(){return a++}
}
var bar = foo()
bar()
console.log(a)//1
bar()
console.log(a)//2

这个例子乍一看有些奇怪,且等我为你一一道来

第一步很简单,我们定义了一个foo函数,在foo函数里定义了一个a变量

第二步,我们return了一个返回值,与往常不同的是,这次返回的值是一个匿名函数,匿名函数中实现了a++的功能

第三步,在下方我们定义了bar,调用foo函数,让它等于foo函数的返回值,也就是我们在第二步中定义的匿名函数,现在我们就可以认为,bar是一个函数,是foo中返回的匿名函数

第四步,我们就可以调用bar方法,实现a++的操作

现在你可能不明白我想要做什么,不要急,思考一个问题:这个例子的作用域链是什么样的呢?试着画一画

答案:

bar被赋值了foo中的匿名函数,bar自然就连接在了foo函数的作用域链上,根据我们上面提到的,作用域链内部可以访问外部,也就是说,bar可以访问foo中的变量a,也可以访问外部的全局变量

那我们为什么不直接在全局中定义a变量,然后对它操作呢?

实际上,如果我们将每个要使用的变量都定义为全局变量,就会导致全局变量过多,这对维护与优化来说并不是好事情

使用了闭包,我们将要使用的变量放在函数作用域中,使用函数作用域返回的函数来操作这个变量,就避免了将变量定义在全局中,在我们不需要它的时候直接为bar赋值为null,没办法再调用到那个匿名函数,程序就会自动检测到并回收foo作用域链,为我们清理出内存

这里对作用域链与闭包只做了简单介绍,像更深入理解请移步红宝书(《JavaScript高级程序设计(第四版)》)

扩展

使用自执函数优化闭包:

var bar = (function(){var a = 0return function(){a++;}
})
bar()
console.log(a)//1
bar()
console.log(a)//2

与前一个例子中不同的是,我没有定义foo函数,直接用一个自执行函数替代了foo,我们需要的只是foo对应的函数作用域链,它本身我们并不关心,所以我们可以像本例这样进行简写

js什么是闭包?简单理解相关推荐

  1. Javascript闭包简单理解

    Javascript闭包简单理解 原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解. 说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和 ...

  2. js的原型链简单理解

    js的原型链 首先,在将js的原型链之前我们先来说一下什么是原型,当我们生成一个普通对象x的时候,这个x会有一个隐藏属性x._proto_,这个属性会指向Object.prototype也就是 x._ ...

  3. 关于Javascript闭包的理解

    一.什么是闭包? "官方"的解释是:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 相 ...

  4. JavaScript闭包函数理解

    JavaScript闭包 一.变量的作用域: 要理解闭包,首先必须理解JavaScript特殊的变量作用域.      变量作用域无非就是两种:全局变量 和 局部变量.       JavaScrip ...

  5. 简单理解js闭包、类型引用....第一章

    js 闭包函数.类型引用.this指向.对象原型链...这些东西让我们对js又爱又恨!js虐我千百遍,我待js如初恋. 很多初学者一开始会觉得这些概念没什么用,导致对这些东西产生一种抵抗力.接下来我们 ...

  6. JS闭包的简单理解。优缺点以及垃圾回收机制

    闭包是什么? ·了解闭包首先了解js的'链式作用域'结构,对象可以一级一级的向上查找父对象的变量,所以父对象的变量对子对象可见,反之不成立:所以都可以访问全局变量 ·为了解决函数外部无法访问函数内局部 ...

  7. JS闭包的理解及常见应用场景

    JS闭包的理解及常见应用场景 一.总结 一句话总结: 闭包是指有权访问另一个函数作用域中的变量的函数 1.如何从外部读取函数内部的变量,为什么? 闭包:f2可以读取f1中的变量,只要把f2作为返回值, ...

  8. 面试官:谈谈对JS闭包的理解及常见应用场景(闭包的作用)

    文章目录 对JS闭包的理解及常见应用场景(闭包的作用) 1.变量作用域 2.如何从外部读取函数内部的变量? 3.闭包概念 4.闭包用途 5.闭包的理解 6.闭包应用场景 setTimeout传参 回调 ...

  9. 谈一谈对JS闭包的理解

    个人觉得理解闭包,首先要理解以下几个概念. 1.函数的作用域和作用域链 js不像java等其他类语言,它并不存在块级作用域,取而代之的是函数作用域,另一个变量作用域是全局作用域. 函数的作用域:变量在 ...

  10. 【学习笔记】深入理解js原型和闭包(5)——instanceof

    又介绍一个老朋友--instanceof. 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/ ...

最新文章

  1. ProjectManagement::Redmine备份与恢复
  2. oracle入门(8)——实战:支持可变长参数、多种条件、多个参数排序、分页的存储过程查询组件...
  3. 转——C# DataGridView控件 动态添加新行
  4. 还以为iPhone安全?多款iOS应用被曝“偷偷”发送用户数据
  5. 【kafka】kafka 2.3 版本的kafka topic 分区扩容
  6. c语言程序优化设计,C程序设计语言的教学策略优化设计
  7. java中class.forName(str)的作用
  8. 计算机辅助翻译工具客户端,OmegaT(计算机辅助翻译软件)
  9. 10000,感谢有你
  10. 03. isasupperclass
  11. RISC-V 指令架构 -- 模式切换
  12. cocos studio 3.10 垂死挣扎版1.1 发布啦,带使用视频
  13. 使用 zx 编写在 Node 中编写 Bash 脚本
  14. 仿简书APP富文本编辑器实现
  15. 【计算机视觉与深度学习】线性分类器(一)
  16. 使用晨曦记账本,根据需要设置记账表格行数
  17. SSH_maven_me
  18. java内存数据被篡改,初涉Java内存模型
  19. 学习笔记lalala
  20. mongodb用哪些端口号_mongodb启动命令与端口设置

热门文章

  1. Unhandled JS Exception: Unexpected identifier ‘_classCallCheck‘. import call expects exactly one arg
  2. PHP中 字符串 常用函数
  3. 【总结】大学四年来,用过的一些网站整理
  4. SitePoint播客#87:MeltSheep和FireRock
  5. Nginx使用stream模块分流实现端口复用
  6. idea设置类注释和方法注释(贼详细)
  7. 基于百度飞桨PaddleOCR的图片文字识别
  8. 微信小程序在小米手机预览头部空白了
  9. Windows CMD 访问UCN路径
  10. 读书笔记:《群论彩图版》