JavaScript之闭包理解
JavaScript之闭包理解
闭包(Closure)是JavaScript学习过程中一个非常重要的问题
闭包和执行上下文和作用域链有着千丝万缕的关系。闭包是指有权访问另一个函数作用域的变量的函数(JavaScript高级程序设计(第三版)P178)
闭包是一个函数,对闭包的一个理解可以是所在的执行上下文已经出栈,但是仍然访问了其所在的执行上下文变量对象的函数
这里所指的所在的执行上下文指的是闭包函数对应的执行上下文,而不是闭包本省所对应的执行上下文
来一个简单的例子
function A(){var a = 2;function B(){console.log(a);}return B;
}
A()();
那么他的执行上下文栈行为是这样的
/*伪代码*/
// 代码执行时最先进入全局环境,全局上下文被创建并入栈
ECStack.push(globalContext);
// A 被调用,A 函数上下文被创建并入栈
ECStack.push(<A> functionContext);
// A 执行完毕,A 函数上下文出栈
ECStack.pop();
// B 被调用,B 函数上下文被创建并入栈
ECStack.push(<B> functionContext);
// B 执行完毕,B 函数上下文出栈
ECStack.pop();
// 代码执行完毕,全局上下文出栈
ECStack.pop();
我们都知道,JavaScript 拥有自动的垃圾回收机制,当一个值失去引用的时候,垃圾回收机制会根据特殊的算法找到它并将其回收。
函数的执行上下文在出栈后,其变量对象会失去引用等待被回收,而闭包的存在会阻止这一过程,因为闭包的作用域链包含了其所在执行上下文的变量对象。
通过上面的代码可以看出,在B函数执行的时候,A的上下文已经出栈了,按照JavaScript的垃圾回收机制,A上下文的变量对象失去引用后会被垃圾回收机制回收,但是由于B上下文作用域链包含了A上下文的变量对象,所以A上下文的变量对象不会被垃圾回收机制回收。
我们知道函数作用域是在函数被定义(声明)的时候确定的。每一个函数都会包含一个[[scope]]内部属性,在函数被定义的时候,该函数的[[scope]]属性会保存其上层上下文的变量对象,形成包含上层上下文变量对象的层级链。
那么刚刚的代码上下文应该是这样的,在创建的时候
B.[[scope]]=[AContext.VO,globalContext.VO]
在B被调用的时候,其执行上下文会被创建并入栈,此时会生成变量对象并将该变量对象添加进作用域链的顶端,并将[[scope]]添加进作用域链
BContext.Scope = [BContext.VO].concat(B.[[scope]])
=>
BContext.Scope = [BContext.VO,AContext.VO,globalContext.VO]
可见,B上下文的作用域链包含了A上下文的变量对象,并且由于B访问A中的变量,阻止了A上下文的变量对象被垃圾回收机制回收。
看一个面试题经常会遇到的一个关于闭包的很经典的题目
var arr = [];
for (var i = 0; i < 3; i++) {arr[i] = function () {console.log(i);};
}arr[0]();//3
arr[1]();//3
arr[2]();//3
在 arr[0] 函数执行之前,我们可以知道,全局上下文的变量对象如下所示
globalContext = {VO:{arr:[],i=3}
}
在 arr[0] 被调用执行时,其作用域链在函数上下文的创建阶段被创建,其作用域链如下
arr[0]Context = {Scope: [arr[0]Context.VO, globalContext.VO]
}
arr[0]函数会在自身变量对象中寻找i(arr总没有i),所以会向上找,找到全局上下文变量对象中的i,所以输出3
那么如何解决闭包
立即执行函数
var arr = [];
for(var i = 0; i < 3; i++){arr[i] = (function(j) {return function(){console.log(j);}})(i)
}
arr[0]();//0
arr[1]();//1
arr[2]();//2
ES6中的let
var arr = [];
for (let i = 0; i < 3; i++) {arr[i] = function () {console.log(i);};
}arr[0]();//0
arr[1]();//1
arr[2]();//2
setTimeout
也还可以利用setTimeout的第三个参数
var arr = [];for(var i = 0; i < 3; i++){arr[i] = setTimeout(function(i){console.log(i);},0,i)}
JavaScript之闭包理解相关推荐
- javascript之闭包理解以及应用场景
1 function fn(){2 var a = 0;3 return function (){4 return ++a;5 } 6 } 如上所示,上面第一个return返回的就是一个闭包,那么本质 ...
- 深入理解javascript的闭包
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域 ...
- 深入理解JavaScript的闭包特性如何给循环中的对象添加事件
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- 【Javascript】深入理解this作用域问题以及new/let/var/const对this作用域的影响
理解this作用域 <javascript高级程序设计>中有说到: this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象调用时,t ...
- JavaScript之全面理解面向对象的JS
今天看到一篇文章写得很好,对于像博主这种js一般级别的菜鸟很有帮助,博主秉着"好文要转"的原则收藏了这篇文章,简单排了下版,分享给大家,本文转自原文:http://www.ibm. ...
- mysql闭包的概念_彻底搞懂JavaScript的闭包、防抖跟节流
最近出去面试了一下,收获颇多!!! 以前的我,追求实际,比较追求实用价值,然而最近面试,传说中的面试造火箭,工作拧螺丝,竟然被我遇到了.虽然很多知识点在实际工作中并不经常用到,但人家就是靠这个来筛选人 ...
- javascript之异步操作理解---回调函数,async,await以及promise对象
javascript之异步操作理解---回调函数,async,await以及promise对象 概述 概述 写在前面:虽然平时做项目,但是发现自己写的代码还是很烂.最近接触了一个对性能要求比较高的项目 ...
- 【javascript】深入理解对象
为什么80%的码农都做不了架构师?>>> 今天学习的主题是 JavaScript对象. 要创建一个JavaScript对象大家应该都觉得很简单,直接写上一行 var obj = ...
- 我对javascript对象的理解
前言 JavaScript这门语言除了基本类型都是对象,可以说JavaScript核心就是对象,因此理解JavaScript对象及其种种特性至关重要,这是内功.本文介绍了我对es5对象,原型, 原型链 ...
- Javascript的闭包及其使用技巧实例
Javascript的闭包及其使用技巧实例 一.闭包的基本概念 闭包(Closure)是一个引用了自由变量的函数,记录了该函数在定义时的scope chain.又称词法闭包(Lexical Closu ...
最新文章
- pytorch 图像归一化
- 获山东科技最高奖-农业大健康·万书波:沉醉谋定花生增产
- 唐岩自述奋斗史:从娄底青年到陌陌上市
- 百练4103:踩方格
- Android官方开发文档Training系列课程中文版:Android的安全建议
- java王大拿谢大脚_《乡村爱情1》:“王大拿”赵本山有3大亮点,追谢大脚根本不算啥...
- canvas笔记-画一个五角星(含算法)
- [转载]Validation of viewstate MAC failed异常的原因及解决方法
- 不止命令行-自定义VS生成事件
- 【python】装饰器的练习题
- git rebase -i之我见
- java程序编译后会产生_java程序编译后会产生什么
- css 设置浏览器最小宽度
- Cknife(中国菜刀) and AntSword(中国蚁剑)使用
- 机器学习之初识SVM
- html生日祝福心形,心形生日祝福QQ空间个性留言代码_送你的
- SwiftUI SF Symbols 好处及其使用指南
- 流利说英语level4_英语流利说懂你英语 Level4 Unit1 Part1 Vocabulary
- 安利几个实用且有趣的Excel实用技巧给你
- 【SEED Labs 2.0】ARP Cache Poisoning Attack Lab