深入理解javascript闭包(一)
闭包(closure)是Javascript语言的一个难点。也是它的特色,非常多高级应用都要依靠闭包实现。
一、什么是闭包?
官方”的解释是:闭包是一个拥有很多变量和绑定了这些变量的环境的表达式(一般是一个函数),因而这些变量也是该表达式的一部分。
相信非常少有人能直接看懂这句话,由于他描写叙述的太学术。事实上这句话通俗的来说就是:JavaScript中全部的function都是一个闭包。只是一般来说,嵌套的function所产生的闭包更为强大。也是大部分时候我们所谓的“闭包”。
要理解闭包,首先必须理解Javascript特殊的变量作用域。
二、变量的作用域
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处。就在于函数内部能够直接读取全局变量。
var n=999;
function f1(){
alert(n);
}
f1(); // 999
还有一方面,在函数外部自然无法读取函数内的局部变量。
function f1(){
var n=999;
}
alert(n); // error
这里有一个地方须要注意,函数内部声明变量的时候。一定要使用var命令。假设不用的话,你实际上声明了一个全局变量!
function f1(){
n=999;
}
f1();
alert(n); // 999
三、怎样从外部读取局部变量?
出于种种原因,我们有时候须要得到函数内的局部变量。可是,前面已经说过了,正常情况下,这是办不到的,仅仅有通过变通方法才干实现。
那就是在函数的内部,再定义一个函数。
function f1(){
n=999;
function f2(){
alert(n); // 999
}
}
在上面的代码中,函数f2就被包含在函数f1内部,这时f1内部的全部局部变量。对f2都是可见的。可是反过来就不行。f2内部的局部变量,对f1 就是不可见的。
这就是Javascript语言特有的“链式作用域”结构(chain scope),
子对象会一级一级地向上寻找全部父对象的变量。所以,父对象的全部变量。对子对象都是可见的,反之则不成立。
既然f2能够读取f1中的局部变量,那么仅仅要把f2作为返回值。我们不就能够在f1外部读取它的内部变量了吗。
function f1(){
n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
上面代码中的f2函数,就是闭包。
各种专业文献上的“闭包”(closure)定义非常抽象,非常难看懂。我的理解是。闭包就是可以读取其它函数内部变量的函数。
因为在Javascript语言中。仅仅有函数内部的子函数才干读取局部变量,因此能够把闭包简单理解成“定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四、闭包的用途
闭包能够用在很多地方。它的最大用处有两个,一个是前面提到的能够读取函数内部的变量。还有一个就是让这些变量的值始终保持在内存中。
怎么来理解这句话呢?请看以下的代码。
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共执行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中。并没有在f1调用后被自己主动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中。而f2的存在依赖于f1,因此f1也始终在内存中。不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中还有一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用varkeyword。因此 nAdd是一个全局变量,而不是局部变量。
其次。nAdd的值是一个匿名函数(anonymous function)。而这个
匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,能够在函数外部对函数内部的局部变量进行操作。
五、使用闭包的注意点
1)因为闭包会使得函数中的变量都被保存在内存中。内存消耗非常大,所以不能滥用闭包。否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量所有删除。
2)闭包会在父函数外部。改变父函数内部变量的值。
所以,假设你把父函数当作对象(object)使用。把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便
改变父函数内部变量的值。
六、思考题
假设你能理解以下代码的执行结果。应该就算理解闭包的执行机制了。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //The Window
七、JavaScript闭包样例
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; //注意这里
}
var obj=outerFun();
obj(); //结果为1
obj(); //结果为2
var obj2=outerFun();
obj2(); //结果为1
obj2(); //结果为2
再次来看什么是闭包?
当内部函数 在定义它的作用域 的外部 被引用时,就创建了该内部函数的闭包 ,假设内部函数引用了位于外部函数的变量,当外部函数调用完成后,这些变量在内存不会被 释放,由于闭包须要它们。
再来看一个样例
function outerFun()
{
var a =0;
alert(a);
}
var a=4;
outerFun();
alert(a);
结果是 0 , 4 . 由于在函数内部使用了varkeyword 维护a的作用域在outFun()内部.
再看以下的代码:
unction outerFun()
{
//没有var
a =0;
alert(a);
}
var a=4;
outerFun();
alert(a);
结果为 0 , 0 真是奇怪,为什么呢?
作用域链是描写叙述一种路径的术语,沿着该路径能够确定变量的值 .当运行a=0时,由于没有使用varkeyword,因此赋值操作会沿着作用域链到var a=4; 并改变其值.
假设你对javascript闭包还不是非常理解,能够再看下篇文章。
深入理解javascript闭包(一)的更多相关文章
- 我从来不理解JavaScript闭包,直到有人这样向我解释它...
摘要: 理解JS闭包. 原文:我从来不理解JavaScript闭包,直到有人这样向我解释它... 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 正如标题所述,JavaScript闭包 ...
- 深入理解JavaScript闭包【译】
在<高级程序设计>中,对于闭包一直没有很好的解释,在stackoverflow上翻出了一篇很老的<JavaScript closure for dummies>(2016)~ ...
- 【转】深入理解JavaScript闭包闭包(closure) (closure)
一.什么是闭包?"官方"的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述 ...
- 全面理解Javascript闭包和闭包的几种写法及用途
好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法.用法和用途. 一.什么 ...
- 深入理解JavaScript闭包(closure)
最近在网上查阅了不少javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Java ...
- 深入理解javascript闭包(一)
原文转自脚本之家(http://www.jb51.net/article/24101.htm) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...
- 深入理解Javascript闭包概念
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部能够直接读取全局变量 ...
- 轻松理解JavaScript闭包
摘要 闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数. 下面列举出常见的闭包实现方式,以例子讲解闭包概念 ...
- 【译】理解JavaScript闭包——新手指南
闭包是JavaScript中一个基本的概念,每个JavaScript开发者都应该知道和理解的.然而,很多新手JavaScript开发者对这个概念还是很困惑的. 正确理解闭包可以帮助你写出更好.更高效. ...
随机推荐
- linux常用指令(飞天云)
1.svn相关指令 svn co svn://... //check out 到本地 2.pangu相关 pu cpdir pangu://... //复制pangu里面对应的文件夹到本 ...
- 如何使rdlc报表的表头在每一页都显示
想要使表格的表头在每一行都显示,直接在表格的属性设置界面中设置是无效的,应该算是一个BUG,如图: 但还是可以实现的,实现方法如下,这个实现方法从网上得到 开发工具: Visual Studio 20 ...
- [r]Seven habits of effective text editing
Seven habits of effective text editing(via) Bram Moolenaar November 2000 If you spend a lot of time ...
- 实现一个简单的sniffer
#include<stdio.h> #include<pcap.h> #include<unistd.h> #include<stdlib.h> //# ...
- oracle常见sql积累
select lower('HELLO') from dual;select lpad(100, 5, '*') from dual;select sysdate + 1 / 24 from dual ...
- PLSQL Developer如何设置自动打开上次编辑的文件
作为开发人员经常把sql语句保存到文件中以方便下次继续使用,问题是plsqlDev重启后每次都需要手工打开这个文件,好不方便: 以下设置是plsqlDev启动后自动打开上次编辑的文件. 选择配置> ...
- 【Linux】基础配置-修改命令提示符的风格
1,效果图: [groot]$ 2,设置步骤: 编辑~/.bashrc文件,在最后增加设置行: #显示当面目录的最后一层目录#PS1='\[\e[32m\][\u@\h \W]$\[\e[m\]'#只 ...
- cf B Three matrices
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; ][] ...
- LXC是如何与CGROUP,namespace扯上关系的?再加上DOCKER.IO。完美!!!
最后还余下网络去攻克了. 不同的模板,只是在同一个LINUX内核上去实现不同的发行版的特性. 终归,都是用同样的内核来实现调度.故而是一个轻量极的方案. 而不像KVM一样,GUEST OS里的CPU也 ...
- LINUX-LXC要好好关注下
因为我觉得轻量极虚拟化可能是云的另一个发展方向. 至少,腾讯的WEB云引擎是以此为基础. LXC结合DOCKER.相信是快速云的另一种实现. 余下的,只是结合生产系统围绕这一中心进行的的二次开了. I ...