js闭包深度讲解
js的闭包是学习js过程中的重点,但是不得不说也是一个难点呀,其涉及到了js中的很多概念。我在学习js中也遇到了很多问题,这篇文章算是一个对闭包的总结,文章主要内容为闭包的基本知识点与对其理解上的一些难点的讲解。
闭包是什么?
闭包就是嵌套在函数里面的内部函数,并且该内部函数可以访问外部函数中声明的所有局部变量、参数和其他内部函数。当该内部函数在外部函数外被调用,就生成了闭包。注意这里的调用两个字,如果内部函数没有被外部函数返回的话就不能被调用了。
function outerFun()
{
var a=0;
function innerFun()
{
a++;
alert(a);
}
}
innerFun();
这里的innerFun就不能被外部调用,因为其在outerFun中定义的,作用域为outerFun的范围。
function outerFun()
{
var a=0;
function innerFun()
{
a++;
alert(a);
}
return innerFun; //注意这里
}
在outerFun内部将innerFun返回到外部就能在外部调用innerFun了。
为什么内部定义的函数只有在返回到外部后才能使用?
这与js的运行机制有关:
如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。
- 当定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。
- 当执行函数a的时候,a会进入相应的执行环境(excution context)。
- 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
- 然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
- 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。
- 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。
到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给全局变量c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,而c又一直存在,这就是为什么函数a执行后不会被回收的原因。
当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:
如图所示,当在函数b中访问一个变量的时候,搜索顺序是:
- 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
- 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
- 如果整个作用域链上都无法找到,则返回undefined。
让我们说的更透彻一些。所谓“闭包”,就是在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层函数体中的临时变量。这使得只要目标 对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只是对应新的值,和上次那次调用的是各自独立的。
下面利用一个实际例子进行分析。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript"><!--
window.onload = function() {
for (var i = 1; i < 4; i++) {
var id = document.getElementById("a" + i);
id.onclick = function(){ alert(i); }
}
}
// --></script>
</head>
<body>
<ul>
<li id="a1">aa</li>
<li id="a2">aa</li>
<li id="a3">aa</li>
</ul>
</body>
</html>
这段代码的实际效果是无论点击哪一个标签都是现实“4”。因为在onload函数后执行完后i的值就是4了,中间变化的过程值没有被内存存储。闭包允许内层函数引用父函数中的变量,但是该变量是最终值
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript"><!--
window.onload = function() {
for (var i = 1; i < 4; i++) {
var id = document.getElementById("a" + i);
id.onclick = (function(i) {
return function() {
alert(i);
}
})(i);
}
}
// --></script>
</head>
<body>
<ul>
<li id="a1">aa</li>
<li id="a2">aa</li>
<li id="a3">aa</li>
</ul>
</body>
</html>
而这段代码的实际效果为点击标签其会显示对应的数字,因为在onload执行的过程中利用了闭包,将每次调用函数产生的结果都保存在了内存(全局变量c)中,三个不同的i值被单独存储了。
闭包的作用?
在动态执行环境中,数据实时地发生变化,为了保持这些非持久型变量的值,我们用闭包这种载体来存储这些动态数据。这就是闭包的作用。也就说遇到需要存储动态变化的数据或将被回收的数据时,我们可以通过外面再包裹一层函数形成闭包来解决。闭包并不能存储动态值,需要在全局作用域中调用才能实现存储功能。
js闭包深度讲解的更多相关文章
- 简单详细讲解js闭包(看完不懂你砍我!!!)
<javascript高级程序设计>中闭包的概念: 闭包,其实是一种语言特性,它是指的是程序设计语言中,允许将函数看作对象,然后能像在对象中的操作般在函数中定义实例(局部)变量,而这些变量 ...
- Google资深工程师深度讲解Go语言完整教程
资源获取链接点击这里 欢迎大家来到深度讲解Go语言的课堂.本课程将从基本语法讲起,逐渐深入,帮助同学深度理解Go语言面向接口,函数式编程,错误处理,测试,并行计算等元素,并带领大家实现一个分布式爬虫的 ...
- js 闭包与垃圾回收-待删
关于闭包请看戳 串讲-解释篇:作用域,作用域链,执行环境,变量对象,活动对象,闭包,本篇写的不太好: 先摆定义: 函数对象,可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种 ...
- webpack与browser-sync热更新原理深度讲解
本文首发于CSDN网站,下面的版本又经过进一步的修订.原文:webpack与browser-sync热更新原理深度讲解本文包含如下内容: webpack-hot-middleware EventSou ...
- js 深入原理讲解系列-currying function
js 深入原理讲解系列-currying function 能看懂这一题你就掌握了 js 科里函数的核心原理 不要专业的术语,说人话,讲明白! Q: 实现 sum 函数使得以下表达式的值正确 cons ...
- js闭包的作用域以及闭包案列的介绍:
转载▼ 标签: it js闭包的作用域以及闭包案列的介绍: 首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...
- 大部分人都会做错的经典JS闭包面试题
由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...
- Js闭包常见三种用法
Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...
- js闭包之初步理解( JavaScript closure)
闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...
随机推荐
- java中的静态代理和动态代理,入门整理
静态代理和动态代理主要解决的问题是:在直接访问对象时带来的问题,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后 ...
- JS日期时间加减实现
首先,上代码 var diffDate = function(date, diff) { return new Date( Date.UTC( date.getUTCFullYear(), date. ...
- Spark工作机制简述
Spark工作机制 主要模块 调度与任务分配 I/O模块 通信控制模块 容错模块 Shuffle模块 调度层次 应用 作业 Stage Task 调度算法 FIFO FAIR(公平调度) Spark应 ...
- UE4里的渲染线程
记的上次看过UniRx里的源代码,说是参考微软的响应式编程框架,响应式编程里的一些理论不细说,只单说UniRx里的事件流里的事件压入与执行,与UE4的渲染线程设计有很多相同之处,如果有了解响应式编程相 ...
- EF6的多线程与分库架构设计实现
1.项目背景 这里简单介绍一下项目需求背景,之前公司的项目基于EF++Repository+UnitOfWork的框架设计的,其中涉及到的技术有RabbitMq消息队列,Autofac依赖注入等常用的 ...
- SQL SERVER将多行数据合并成一行(转载)
昨天遇到一个SQL Server的问题:需要写一个储存过程来处理几个表中的数据,最后问题出在我想将一个表的一个列的多行内容拼接成一行 比如表中有两列数据 : ep_classes ep_name A ...
- 毕向东udp学习笔记3多线程聊天
项目功能: 实现了多线程下的发送接收,比较好 希望可以加入GUI,类似聊天软件一样,有一个消息输入框,捕获输入消息,作为发送线程 有一个显示消息框,接收消息并显示,作为接收线程 不知道的是,当在线程中 ...
- C语言 extern学习1
没有头文件时,通过本文件内的函数声明来确定定义域,实现功能: //单文件测试 #include <stdio.h> /* 经测试,C语言环境下子函数默认是void型:所以可省略不写 为严谨 ...
- 面向UI编程:ui.js 1.1 使用观察者模式完成组件之间数据流转,彻底分离组件之间的耦合,完成组件的高内聚
开头想明确一些概念,因为有些概念不明确会导致很多问题,比如你写这个框架为什么不去解决啥啥啥的问题,哎,心累. 什么是框架? 百度的解释:框架(Framework)是整个或部分系统的可重用设计,表现为一 ...
- JavaScript 事件总结
本文总结自<JavaScript高级程序设计>以及自己平时的经验,针对较新浏览器以及 DOM3 级事件标准(2016年8月),对少部分内容作了更正,增加了各种例子及解析. 如无特殊说明,本 ...