JS之闭包详细解读
闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。
1.变量作用域
全局变量:所有的函数外部定义的变量,它的作用域是整个script。
局部变量:定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效。再调用就是出错。
举例如下-局部变量:
<script type="text/javascript">
function fun(){
var a = 100;
}
console.log(a);
</script>
a变量定义在fun函数内,是局部变量,所以它不能在外部被访问。

举例如下-全局变量:
<script type="text/javascript">
var c = 100;
function fun(){
var a = 100;
console.log(c)
}
fun();
console.log(c);
</script>
在全局定义一个全局变量c,不仅能在fun函数内部被访问,在函数外依旧能被访问。

2.间接访问局部变量
<script type="text/javascript">
function fun(){
var a = 100;
function fun1(){
console.log(a);
}
fun1();
}
fun();
</script>
通过调用fun1把a打印出来,fun1是可以访问fun的所有变量

3.作用域链
可以参考我的这篇文章JS之预编译和执行顺序(全局和函数)可以更好的理解预编译的原理,为作用域链做准备。
举例:
<script type="text/javascript">
var a = 100;
function fun(){
var b = 200
function fun2(){
var c = 300
}
function fun3(){
var d = 400
}
fun2()
fun3()
}
fun()
</script>
首先预编译,一开始生成一个GO{
a:underfined
fun:function fun(){//fun的函数体
var b = 200
function fun2(){
var c = 300
}
function fun3(){
var d = 400
}
fun2()
fun3()
}
}
逐行执行代码,GO{
a:100
fun:function fun(){//fun的函数体
var b = 200
function fun2(){
var c = 300
}
function fun3(){
var d = 400
}
fun2()
fun3()
}
}
当fun函数执行时,首先预编译会产生一个AO{
b:underfined
fun2:function fun2(){
var c = 300
}
fun3:function fun3(){
var d = 400
}
}
这里注意的是fun函数是在全局的环境下产生的,所以自己身上挂载这一个GO,由于作用域链是栈式结构,先产生的先进去,最后出来,
在这个例子的情况下,AO是后于GO产生的,所以对于fun函数本身来说,执行代码的时候,会先去自己本身的AO里找找看,如果没有找到要用的东西,就去父级查找,此题的父级是GO
此刻fun的作用域链是 第0位 fun的AO{}
第1位 GO{}
fun函数开始逐行执行AO{
b:200
fun2:function fun2(){
var c = 300
}
fun3:function fun3(){
var d = 400
}
}
注意:函数每次调用才会产生AO,每次产生的AO还都是不一样的
然后遇到fun2函数的执行,预编译产生自己的AO{
c:underfined
}
此刻fun2的作用域链是第0位 fun2的AO{}
第1位 fun的AO{}
第2位 GO{}
然后遇到fun3函数的执行,预编译产生自己的AO{
d:underfined
}
此刻fun3的作用域链是第0位 fun3的AO{}
第1位 fun的AO{}
第2位 GO{}
fun2和fun3的作用域链没有什么联系
当函数fun2和fun3执行完毕,自己将砍掉自己和自己的AO的联系,
最后就是fun函数执行完毕,它也是砍掉自己和自己AO的联系。
这就是一个我们平时看到不是闭包的函数。
4.闭包
1.闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。
2.写法:
<script type="text/javascript">
function fun1(){
var a = 100;
function fun2(){
a++;
console.log(a);
}
return fun2;
} var fun = fun1();
fun()
fun()
</script>
3.效果如下:

4.分析:
执行代码
GO{
fun:underfined
fun1:function fun1()
{
var a = 100;
function fun2()
{
a++;
console.log(a);
}
return fun2;
}
}
然后第十一行开始这里,就是fun1函数执行,然后把fun1的return赋值给fun,这里比较复杂,我们分开来看,
这里fun1函数执行,产生AO{
a:100
fun2:function fun2(){
a++;
console.log(a);
}
}
此刻fun1的作用域链为 第0位 AO
第1位 GO
此刻fun2的作用域链为 第0位 fun1的AO
第1位 GO
解释一下,fun2只是声明了,并没有产生调用,所以没有产生自己的AO,
正常的,我们到第7行代码我们就结束了,但是这个时候来了一个return fun2,把fun2这个函数体抛给了全局变量fun,好了,fun1函数执行完毕,消除自己的AO,
此刻fun2的作用域链为 第0位 fun1的AO
第1位 GO
第十二行就是fun执行,然后,它本身是没有a的,但是它可以用fun1的AO,然后加,然后打印,
因为fun中的fun1的AO本来是应该在fun1销毁时,去掉,但是被抛给fun,所以现在fun1的AO没办法销毁,所以现在a变量相当于一个只能被fun访问的全局变量。
所以第十三行再调用一次fun函数,a被打印的值为102.
5.闭包之深入理解
举例1:
<script type="text/javascript">
function fun1(){
var a = 100;
function fun2(){
a ++;
console.log(a);
} return fun2;
}
var fn1 = fun1(); //生成自己的AO,上面有a
var fn2 = fun1();
fn1()//
fn1()//
fn2()//
</script>
fn1和fn2互不干涉,因为fun1函数调用了两次,所以两次的AO是不一样的。
举例2:
<script type="text/javascript">
function fun(){
var num = 0;
function jia(){
num++;
console.log(num);
}
function jian(){
num--;
console.log(num)
}
return [jia,jian];
}
var fn = fun();
var jia = fn[0];
var jian = fn[1];
jia()//
jian()//
</script>
jia和jian共用一个fun的AO,一动全都动,十二行返回了一个数组,
举例3:
<script type="text/javascript">
function fun(){
var num = 0;
function jia(){
num++;
console.log(num);
}
function jian(){
num--;
console.log(num)
}
return [jia,jian];
}
var jia = fun()[0];
var jian = fun()[1];
jia()//
jian()//-1
</script>
这里有一个坑,jia = fun()[0]; jian = fun()[1];fun函数执行了两遍,所以两次的AO不一样,所以jia和jian操作的对象不一样。
6.闭包好处与坏处
好处:
①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
③匿名自执行函数可以减少内存消耗
坏处:
①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;
②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响
7.闭包解决的问题
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
<script type="text/javascript">
var lis = document.getElementsByTagName("li");
for(var i = 0;i < lis.length;i++){
lis[i].onclick = function(){
console.log(i)
} }
</script>
</body>
</html>

不管点击哪个都是10,那是因为点击事件是我们点击才触发的函数,等到触发的时候,i早就变成10跳出循环了,,这个时候我们就需要立即执行函数,创造了十个不同的作用域
解决方案:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
<script type="text/javascript">
var lis = document.getElementsByTagName("li");
for(var i = 0;i < lis.length;i++){
// lis[i].onclick = function(){
// console.log(i)
// }
(function(i){
lis[i].onclick = function(){
console.log(i)
}
})(i)
}
</script>
</body>
</html>
JS之闭包详细解读的更多相关文章
- JS中的闭包 详细解析大全(面试避必考题)
JS中闭包的介绍 闭包的概念 闭包就是能够读取其他函数内部变量的函数. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变 ...
- JS内存空间详细图解
JS内存空间详细图解 变量对象与堆内存 var a = 20; var b = 'abc'; var c = true; var d = { m: 20 } 因为JavaScript具有自动垃圾回收机 ...
- MemCache超详细解读
MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高 ...
- 关于js中闭包的理解
1.以前很不理解js中闭包的概念及使用,下面来看一下 function foo() { var a = 123; var b = 456; return function () { return a; ...
- js的闭包
一,关于js闭包的只是感觉很高大上似乎,对于学弱来说任何问题都是这样的,值得去钻研和提高. 资料上理解的都是关于js的闭包其实就是js的变量的作用域的灵活使用. 函数内部定义变量的时候,一定要用 va ...
- MemCache超详细解读 图
http://www.cnblogs.com/xrq730/p/4948707.html MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于 ...
- 彻底搞清js中闭包(Closure)的概念
js中闭包这个概念对于初学js的同学来说, 会比较陌生, 有些难以理解, 理解起来非常模糊. 今天就和大家一起来探讨一下这个玩意. 相信大家在看完后, 心中的迷惑会迎然而解. 闭包概念: 闭包就是有权 ...
- rpm软件包管理的详细解读
CentOS系统上使用rpm命令管理程序包:安装.卸载.升级.查询.校验.数据库维护 1.基本安装 rpm -ivh PackageFile 2.rpm选项 rpm -ivh --test Packa ...
- MemCache详细解读
MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高 ...
随机推荐
- Qt之QStackedWidget
简述 QStackedWidget继承自QFrame. QStackedWidget类提供了多页面切换的布局,一次只能看到一个界面. QStackedWidget可用于创建类似于QTabWidget提 ...
- Android基础笔记(十三)- 内容提供者原理和简单使用
为什么要有内容提供者 内容提供者的工作原理 使用内容解析者对内容提供者进行增删改查操作 利用内容提供者和内容解析者备份手机短信 利用内容提供者插入短信 为什么要有内容提供者 内容提供者技术的目的是: ...
- [TypeScript] Asynchronous Iteration using for-await-of
The for-await-of syntax is similar to the for-of iteration. The key difference is that it automatica ...
- hdoj--5563--Clarke and five-pointed star(简单几何)
Clarke and five-pointed star Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K ( ...
- 面向对象(OOP)五大基本原则
书单 <Object-Oriented Analysis & Design with Application>:Grady Booch, 下载地址:object-oriented- ...
- Linux-php安装mongodb
Linux-php安装mongodb 标签(空格分隔): php 安装mongodb 1 下载解压 下载压缩包 :https://www.mongodb.com/download-center?jmp ...
- 减少XML文件数
在android开发中,做出漂亮的ui的应用,往往有数量庞大的xml文件.比如,我们要给一个Button加上一个selector,如果背景不是图片,就得写三个xml文件,分别是:edit_focuse ...
- 3、Go Exit
package main import ( "fmt" "os") func main() { //当使用`os.Exit`的时候defer操作不会被运行 所以 ...
- caffe(7) solver及其配置
solver算是caffe的核心的核心,它协调着整个模型的运作.caffe程序运行必带的一个参数就是solver配置文件.运行代码一般为 # caffe train --solver=*_slover ...
- CF732F Tourist Reform(边双联通)
题意 在一张有向图中,设 ri 为从点 i 出发能够到达的点的数量. 定义有向图的“改良值”为 ri 的最小值. 现给出一张无向图,要求给每条边定一个方向,使产生的有向图“改良值”最大. 输出 最大改 ...