JavaScript ——闭包理解
昨天晚上听别人谈起闭包这个东西,虽然对js有一点了解但却丝毫没有印象,今天也没什么事就顺便研究了一下满足好奇宝宝。整合于网上的理解,记录一下。
一、闭包的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
- 全局作用域
- 局部作用域 :一个 function 形成一个独立的作用域, 而且方法作用域还能够嵌套.
与别的语言不同的是: 花括号({})不能形成一个独立的作用域, 例如Java中的作用域.
下面我们举例说说作用域
var m = 1;
function f(){
//这里面就形成了一个方法作用域, 能够保护其中的变量不能被外部访问
//方法作用域能够访问全局作用域
var n = 2;
alert(m);//会弹出m值
// 嵌套方法作用域
function f1(){
// 这里面再度形成了一个方法作用域
// 其中可以访问外部的那个方法作用域
var n1 = 3;
alert(n);
}
//出了f1作用域就不能访问f1的变量了
//alert(n1); 报错
}
二、闭包
弄清楚作用域的问题后, 我们就可以开始聊聊闭包了。
我们以最经典的for循环为例来讲解. 大家可以试试下面这段代码, 取自JavaScript 秘密花园循环中的闭包
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
输出结果为 10个10 ;
如果在运行前, 你没有猜到正确答案, 那就对了...
- 首先说说为什么最终输出的是10次10, 而不是你想象中的 0, 1, 2, 3, 4, 5, 6, 7, 8,9
因为setTimeout是异步的!
你可以想象由于setTimeout是异步的, 因此我们将这个for循环拆成2个部分
第一个部分专门处理 i 值的变化, 第二个部分专门来做setTimeout
因此我们可以得到如下代码:
// 第一个部分 i++; ... i++; // 总共做10次 // 第二个部分 setTimeout(function() { console.log(i); }, 1000); ... setTimeout(function() { console.log(i); }, 1000); // 总共做10次这样一拆后, 我相信你肯定知道之前那个for循环的运行结果了.
由于循环中的变量 i 一直在变, 最终会变成10, 而循环每每执行setTimeout时, 其中的方法还没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了!
i 变化的整个过程是瞬间完成的, 总之比你异步要快, 就算你setTimout是0毫秒也一样, 会先于你执行完成.
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 0); }- 那么为什么setTimeout中匿名function没有形成闭包呢?
因为setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 因此 i 变化时, 也影响到了匿名function.
因此如果我们定义一个外部函数, 让 i 作为参数传入即可"闭包"我们要的变量了!!
for (var i = 0; i < 10; i++) {
// 注意关键是我们把想要闭包的值当参数传入一个方法
// 这个方法 return 一个新的方法 -- 闭包!!
setTimeout(fn(i), 1000);
}
function fn() { // 为了深刻理解闭包, 这个函数我没有用参数
// 神奇的"闭包"发生在这一步, 其实就是作用域和值复制在起了关键作用,
// 对于数字/字符等类型是复制值, 而不是引用
var a = arguments[0];
return function() {
console.log(a); // 注意现在我操作的变量已经变成 a 了,
// 已经和 i 没有半毛线关系了!
// 而 a 的值就是当时执行时赋予的一个确定值,
// 不会因 i 的变化而变化了!
};
}
- 再换成更简洁的方式看你能不能真正理解闭包
for (var i = 0; i < 10; i++) {
(function(a) {
// 变量 i 的值在传递到这个作用域时被复制给了 a,
// 因此这个值就不会随外部变量而变化了
setTimeout(function() {
console.log(a);
}, 1000);
})(i); // 我们在这里传入参数来"闭包"变量
}
这就是闭包, 简单点说就是专门用来"包养"变量的.
总结:
(1)闭包是一种设计原则,它通过分析上下文,来简化用户的调用,让用户在不知晓的情况下,达到他的目的;
(2)网上主流的对闭包剖析的文章实际上是和闭包原则反向而驰的,如果需要知道闭包细节才能用好的话,这个闭包是设计失败的;
(3)尽量少学习。
JavaScript ——闭包理解的更多相关文章
- javascript闭包理解
//闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...
- 对于 Javascript 闭包理解
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...
- javascript 闭包理解
摘自:http://www.cnblogs.com/jkswjw/p/3180384.html javascript 闭包基础分享 闭包向来给包括JavaScript程序员在内的程序员以神秘,高深的感 ...
- JavaScript闭包理解【关键字:普通函数、闭包、解决获取元素标签索引】
以前总觉得闭包很抽象,很难理解,所以百度一下"闭包"概览,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的 ...
- JavaScript闭包理解的关键 - 作用域链
阮一峰的一篇文章已经对闭包的用途.概念讲解地相对清晰了. 闭包就是能够读取其他函数内部变量的函数. 但我认为里面对于作用域链的解释还不够清晰,这里作一些补充. 闭包之所以可以读取外部函数的内部变量,即 ...
- 关于javascript闭包理解
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一:关于变量的作用域 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. ...
- JavaScript闭包理解【关键字:普通函数、变量访问作用域、闭包、解决获取元素标签索引】
一.闭包(Closure)模糊概述 之前总觉得闭包(Closure)很抽象而且难理解,百度一下"闭包"名词,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代 ...
- javascript 闭包理解例子
function Jquery(){ this.name = 'ysr'; this.sex = 'man'; return { x: this, age : 26 } } var b = new J ...
- javascript深入理解js闭包
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...
随机推荐
- flume+hadoop
摘自:http://www.kankanews.com/ICkengine/archives/130646.shtml
- 【SQL】SQL中笛卡尔积、内连接、外连接的数据演示
SQL的查询语句中,常使用到内连接.外连接,以及连接的基础--笛卡尔积运算. 在简单的SQL中,也许我们还分辨清楚数据如何连接,一旦查询复杂了,脑子也犯浆糊了,迷迷糊糊的. 本文,简单以数据形式记录连 ...
- HDU2112 HDU Today 最短路+字符串哈希
HDU Today Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- Linux下指定版本编译安装LAMP
说明: 操作系统:CentOS 6.5 64位 需求: 编译安装LAMP运行环境 各软件版本如下: MySQL:mysql-5.1.73 Apache:httpd-2.2.31 PHP:php-5.2 ...
- 编写一个Car类,具有final类型的属性品牌,具有功能drive; 定义其子类Aodi和Benchi,具有属性:价格、型号;具有功能:变速; 定义主类E,在其main方法中分别创建Aodi和Benchi的对象并测试对象的特 性。
package b; public class Car { public final static Car pinpai=new Car(); public static Car instance() ...
- serialVersionUID
serialVersionUID作用: 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 有两种生成方式: 一个是默认的1L,比如:private static final l ...
- DevOps到底是什么?
本篇将讨论DevOps到底包含什么,今后的运维工程师应该朝什么方向努力.
- java正则API简单解析
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. 1.简介: java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. ...
- monkeyrunner自动登录脚本
自己写了个平时测试的app的自动登录脚本,亲测可运行.读者参照时只需要改包名.activity名称.坐标值.账号和密码即可 查看坐标是多少的方法:使用手机的指针位置来实现:系统设置---开发者选项-- ...
- Spring 读书笔记-----使用Spring容器(一)
pring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口.他们都可代表Spring容器,Spri ...