深刻理解JavaScript---闭包
闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。——这句话其实有点难以理解。我觉得应该用一些例子来理解闭包的含义。
- 闭包#1
先来看一个函数:
1
2
3
4
5
6
|
function f(){
var b = "b";
return function(){
return b;
}
}
|
这个函数含有一个局部变量b,它在全局空间里面是不可见的。而f()的返回值是另一个匿名函数,该函数有自己的私有的空间,可以访问f()的空间和全局空间,所以b对它来说是可见的。
1
2
|
var n = f();
n();//此时控制台输出"b"
|
因为f()是一个全局函数,它可以在全局空间被调用,当然也可以将其返回值赋值给另一个全局变量n,从而生成一个可以访问f()私有空间的新的全局函数。
- 闭包#2
再来看看下面这个跟上面差不多的函数,在这里f()不再返回函数了,而是直接在函数体内创建一个新的全局函数
1
2
3
4
5
6
7
8
9
|
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
f();
n();//调用f(),然后再调用n(),控制台输出"b"
|
由于n()没有使用var语句,因此它是全局的,同时它也可以访问f()的作用域,所以哪怕n()最后升级为全局函数,但它依然可以保留对f()作用域的访问权。
- 闭包#3
函数通常会让自身的参数视为局部变量,所以我们创建返回函数时,也可以令其返回父级函数的参数,例如:
1
2
3
4
5
6
7
8
9
|
function f(arg){
var n = function(){
return arg;
};
arg++;
return n;
}
var m = f(123);
m();//调用函数,输出124
|
有一点很重要:n被赋值时函数并没有被调用,调用发生是在n被求值,也就是执行return n;语句时。由此可以看出,函数所绑定的是作用域本身,而不是该作用域中的变量或变量当前所返回的值。
- 循环中的闭包#4
这种闭包所导致的bug往往很难被发现,因为它们表面上看起来很正常,来看一下下面的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function f(){
var a = [];
var i;
for (i = 0; i < 3; i++){
a[i] = function(){
return i;
}
}
return a;
}
//下面来运行一下函数,并将结果赋值给数组a
var a = f();
a[0]();//输出3
a[1]();//输出3
a[2]();//输出3
|
为啥不是0、1、2呢?为啥会这样呢?原来在这里创建的三个闭包,它们都指向了一个共同的局部变量i,但是,闭包不会记录它们的值,它们所拥有的的只是一个i的连接(即引用),因此只能返回i当前值,因为i结束循环时值为3,所以这三个函数都指向一个共同值3
如何纠正?显然,需要a[i]指向三个不同的变量,下面是解决方案之一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function f(){
var a = [];
var i;
for (i = 0; i < 3; i++){
a[i] = (function(x){
return function(){
return x;
}
})(i);
}
return a;
}
//下面来运行一下函数,并将结果赋值给数组a
var a = f();
a[0]();//输出0
a[1]();//输出1
a[2]();//输出2
|
这里使用了自调函数,不再直接返回i的值,而是将i传递给自调函数,i赋值给了局部变量x,这样一来,每次迭代x就会拥有各自不同的值了。
解决方案二:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function f(){
function aa(x){
return function(){
return x;
}
}
var a = [];
var i;
for (i = 0; i < 3; i++){
a[i] = aa(i);
}
return a;
}
|
方案二不使用自调函数,而是定义了一个内部函数实现相同的功能,每次迭代操作中,将i的值“本地化”。
- Getter与Setter、 迭代器 闭包的应用示例#5
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var getValue, setValue;
(function(){
var temp = 0;
getValue = function(){
return temp;
};
setValue = function(v){
temp = v;
}
})();
getValue();//输出0
setValue(123)
getValue();//输出123
|
这个例子是通过一个匿名自调函数来实现的,定义的全局函数setValue()和getValue(),确保局部变量temp的不可直接访问性。
下面是一个接受数组输入的初始化函数,其中定义了一个私有指针,该指针会指向数组中的下一个元素。
1
2
3
4
5
6
7
8
9
10
11
12
|
function setup(x){
var i = 0;
return function() {
return x[i++];
};
}
//调用setup(),创建我们所需的next()函数
var next = setup(['a', 'b', 'c']);
//重复调用next(),就可以不停的获取下一个元素
next();//输出"a"
next();//输出"b"
next();//输出"c"
|
到这里闭包就暂时告一段落了,以后有新的理解再写。
深刻理解JavaScript---闭包的更多相关文章
- 我从来不理解JavaScript闭包,直到有人这样向我解释它...
摘要: 理解JS闭包. 原文:我从来不理解JavaScript闭包,直到有人这样向我解释它... 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 正如标题所述,JavaScript闭包 ...
- 【转】深入理解JavaScript闭包闭包(closure) (closure)
一.什么是闭包?"官方"的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述 ...
- 深入理解JavaScript闭包【译】
在<高级程序设计>中,对于闭包一直没有很好的解释,在stackoverflow上翻出了一篇很老的<JavaScript closure for dummies>(2016)~ ...
- 全面理解Javascript闭包和闭包的几种写法及用途
好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法.用法和用途. 一.什么 ...
- 深入理解JavaScript闭包(closure)
最近在网上查阅了不少javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Java ...
- 深入理解javascript闭包(一)
闭包(closure)是Javascript语言的一个难点.也是它的特色,非常多高级应用都要依靠闭包实现. 一.什么是闭包? 官方"的解释是:闭包是一个拥有很多变量和绑定了这些变量的环境的表 ...
- 深入理解javascript闭包(一)
原文转自脚本之家(http://www.jb51.net/article/24101.htm) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...
- 深入理解Javascript闭包概念
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部能够直接读取全局变量 ...
- 轻松理解JavaScript闭包
摘要 闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数. 下面列举出常见的闭包实现方式,以例子讲解闭包概念 ...
- 【译】理解JavaScript闭包——新手指南
闭包是JavaScript中一个基本的概念,每个JavaScript开发者都应该知道和理解的.然而,很多新手JavaScript开发者对这个概念还是很困惑的. 正确理解闭包可以帮助你写出更好.更高效. ...
随机推荐
- Linux操作系统启动流程
一般来说,所有的操作系统的启动流程基本就是: 总的来说,linux系统启动流程可以简单总结为以下几步:1)开机BIOS自检,加载硬盘.2)读取MBR,进行MBR引导.3)grub引导菜单(Boot L ...
- LSTM block和cell区别
LSTM的结构中每个时刻的隐层包含了多个memory blocks(一般我们采用一个block),每个block包含了包含一个Cell(有多个memory cell组成)和三个gate,一个基础的结构 ...
- Node.js中测试mysql的代码var client = mysql.createClient运行出错:TypeError: Object # has no method ‘createClient’
今天在WebStorm下熟悉一个node.js的项目,配置环境时,手一抖,将mysql包从0.8升级到了2.1.1,结果再运行时就出错了. [Fri Mar 14 2014 17:05:49] 连接数 ...
- ogre3D学习基础7---材质详解
物体着色的基础 --- 四种不同光照作用 1.环境反射 近似的模拟了场景中的全局辐射,也就是用来近似模拟所有光在场景中不断散射的结果.材质中有相应的属性来代表这种环境反射颜色. 2.漫反射 这种颜色是 ...
- python 学习分享-购物车实操篇
程序要求如下: '''购物车程序: 启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够,够就直接扣款,不够就 ...
- Baum-Welch算法(EM算法)对HMM模型的训练
Baum-Welch算法就是EM算法,所以首先给出EM算法的Q函数 \[\sum_zP(Z|Y,\theta')\log P(Y,Z|\theta)\] 换成HMM里面的记号便于理解 \[Q(\lam ...
- Coursera无法观看课程解决方案
Coursera无法观看课程解决方案 最近Cousera一直表现不佳,课程视频无法观看.小编结合网上找到的信息,操作一番便解决了问题,视频也可以正常观看了. 首先是win+s找到记事本,并用管理员身份 ...
- JDBC 学习笔记(五)—— Statement
JDBC 使用 Statement 作为 SQL 语句的执行器. Statement 通过 Connection.createStatement() 方法创建,一共支持以下6种方式执行 SQL 语句: ...
- kb-07-RMQ线段树--07(动态规划)
RMQ是一类解决区间最值查询的算法的通称:.一共有四类:在代码中有说明: 下面是ST算法,就是动态规划做法: 来看一下ST算法是怎么实现的(以最大值为例): 首先是预处理,用一个DP解决.设a是要求区 ...
- linux maven安装(三)
1.下载maven http://maven.apache.org/download.cgi 我下载的是:apache-maven-3.3.9-bin.tar.gz 解压:tar -zxvf apac ...