JS闭包—你不知道的JavaScript上卷读书笔记(二)
关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解。 让我们一起来揭开闭包神秘的面纱。
闭包晦涩的定义
看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂。让不少人以为闭包是多么玄乎的东西。在我看过的所有书籍中,我更喜欢《你不知道的javascript(上卷)》的定义:
当函数可以记住并访问所在的词法作用域时,就产生了闭包,或者说函数在创建时的词法作域之外执行。
通俗的来说(不严谨): 就是函数套函数,子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量。子函数如果不被销毁,整条作用域链上的变量仍然保存在内存中。
关于词法作用域,这里不做过多的解释,详情参考:http://www.cnblogs.com/ylweb/p/7531259.html.
下面用一些代码来解释这个定义:
function foo() {var a = 2;function bar() {console.log( a ); // 2}bar();
}foo();
严格来说这段代码并没有形成闭包,因为bar是在创建时所在的词法作用域执行。bar() 对a 的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分。从学术的角度说:,在上面的代码片段中,函数bar() 具有一个涵盖foo() 作用域的闭包
(事实上,涵盖了它能访问的所有作用域,比如全局作用域)。也可以认为bar() 被封闭在
了foo() 的作用域中。为什么呢?原因简单明了,因为bar() 嵌套在foo() 内部。
下面我们来看一段代码,清晰地展示了闭包:
function foo() {var a = 2;function bar() {console.log( a );}return bar;
}var baz = foo();
baz(); // 2 —— 朋友,这就是闭包的效果。
函数bar() 的词法作用域能够访问foo() 的内部作用域。然后我们将bar() 函数本身当作一个值类型进行传递。在这个例子中,我们将bar 所引用的函数对象本身当作返回值。
在foo() 执行后,其返回值(也就是内部的bar() 函数)赋值给变量baz 并调用baz(),实际上只是通过不同的标识符引用调用了内部的函数bar()。
bar() 显然可以被正常执行。但是在这个例子中,它在自己定义的词法作用域以外的地方执行。
在foo() 执行后,通常会期待foo() 的整个内部作用域都被销毁,因为我们知道引擎有垃圾回收器用来释放不再使用的内存空间。由于看上去foo() 的内容不会再被使用,所以很自然地会考虑对其进行回收。
而闭包的“神奇”之处正是可以阻止这件事情的发生。事实上内部作用域依然存在,因此没有被回收。谁在使用这个内部作用域?原来是bar() 本身在使用。
拜bar() 所声明的位置所赐,它拥有涵盖foo() 内部作用域的闭包,使得该作用域能够一直存活,以供bar() 在之后任何时间进行引用。
bar() 依然持有对该作用域的引用,而这个引用就叫作闭包。
循环与闭包
以下是一个最常见的for循环例子:
for (var i=1; i<=5; i++) {setTimeout( function timer() {console.log( i );}, i*1000 );
}
正常情况下,我们对这段代码行为的预期是分别输出数字1~5,但实际上,这段代码在运行时输出五次6。 Why?
首先解释6 是从哪里来的。这个循环的终止条件是i 不再<=5。条件首次成立时i 的值是6。因此,输出显示的是循环结束时i 的最终值。
仔细想一下,这好像又是显而易见的,延迟函数的回调会在循环结束时才执行。事实上,当定时器运行时即使每个迭代中执行的是setTimeout(.., 0),所有的回调函数依然是在循环结束后才会被执行,因此会每次输出一个6 出来。
问题实质:
我们试图假设循环中的每个迭代在运行时都会给自己“捕获”一个i 的副本。但是根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
改进方案:
for (var i=1; i<=5; i++) {(function(j) {setTimeout( function timer() {console.log( j );}, j*1000 );})( i );
}
ES6改进:
for (let i=1; i<=5; i++) {setTimeout( function timer() {console.log( i );}, i*1000 );
}
转载于:https://www.cnblogs.com/ylweb/p/7804309.html
JS闭包—你不知道的JavaScript上卷读书笔记(二)相关推荐
- 你不知道的JavaScript 上卷读书笔记
看了<你不知道的JavaScript 上>,为了防止自己忘记,特此记下与我而言的部分重点 任何足够先进的技术都和魔法无异. --Arthur C. Clarke 作用域和闭包 编译原理 分 ...
- 【你不知道的JavaScript上卷】——作用域与闭包
原文: [你不知道的JavaScript上卷]--作用域与闭包 JS语言万变不离其宗,其中最常用.最重要的也就是常用的几个大概念.数据类型.作用域.原型链.闭包.this指针.异步,不同的人理解不一样 ...
- JavaScript设计模式读书笔记(四)= 技巧型设计模式
全系列目录 JavaScript设计模式读书笔记(一)=> 创建型设计模式 JavaScript设计模式读书笔记(二)=> 结构型设计模式 JavaScript设计模式读书笔记(三)=&g ...
- JavaScript设计模式读书笔记(一)= 创建型设计模式
全系列目录 JavaScript设计模式读书笔记(一)=> 创建型设计模式 JavaScript设计模式读书笔记(二)=> 结构型设计模式 JavaScript设计模式读书笔记(三)=&g ...
- 你不知道的javascript上卷
你不知道的javascript上卷 作用域 javascript是一门编译语言,它不是提前编译的,编译结果也不能在分布式系统中移植.编译的步骤一般如下: 分词/词法分析 词法分析是有状态的判断一个分词 ...
- oracle直查和call哪个更快,让oracle跑的更快1读书笔记二
当前位置:我的异常网» 数据库 » <>读书笔记二 <>读书笔记二 www.myexceptions.net 网友分享于:2013-08-23 浏览:9次 <> ...
- 《How Tomcat Works》读书笔记(二)
<How Tomcat Works>读书笔记(二) 这是<How Tomcat Works>第一二章的读书笔记.第一张主要写了一个静态资源处理的web服务器,第二章加了对ser ...
- 3D游戏设计读书笔记二
3D游戏设计读书笔记二 一.简答题 • 解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系. GameObjects是一个具体的实例,Assets是包括诸多游戏素材的资 ...
- 《Docker 技术入门与实践》-读书笔记二
<Docker 技术入门与实践>-读书笔记一 <Docker 技术入门与实践>-读书笔记二 一.数据管理 用户在使用 Docker 的过程中,往往需要能查看容器内应用产生的数据 ...
最新文章
- SQLServer之创建显式事务
- 重载session存储方式–session_set_save_handler()
- base标签在ie6下的恶心问题
- Android常见命令
- python zipfile 乱码_python zipfile文件名乱码问题
- android奔溃日志手机查看
- js定时器异步请求时候 上一个请求没有响应时下一个请求已经开始_关于异步的理解...
- linux运行非法指令,illegal instruction非法指令的解决思路
- 小程序啦啦外卖、码科跑腿、云贝外卖各种问题汇总解决对策
- html 下拉怎么固定表头,HTML Table 固定表头简单实现
- 一款高颜值的词云包让我拍案叫绝
- android 面部识别解锁,面部识别解锁软件
- matlab里面sin函数是角度,matlab-如何用matlab编写sin函数要求定义一 – 手机爱问
- 包装类的自动装箱,自动拆箱
- Cannot open D:\Anaconda3\Scripts\pip-script.py 解决
- dede 百度主动推送插件
- 一个人能够哲学思考的条件
- Hold不住的老师 HNUST 1588
- 微信小程序之某荟团JS逆向
- kalman滤波总结
热门文章
- 高龄申请海外计算机科学博士,【原创】牛津在读小博谈谈在海外攻读生命科学专业的一些心得...
- Mybatis Plus————代码生成器
- LeetCode算法入门- Compare Version Numbers -day14
- datagridview设置为勾选才可编辑_使用lightroom前,做好这几项设置,有助于更好使用...
- 绿盟漏洞扫描_主机安全漏洞解决方案
- java摘要_java 摘要
- 使用Anaconda3安装pytorch、paddle环境并在pycharm里面进行环境测试
- python类和oop基础知识
- python切片原理_分析python切片原理和方法
- 计算机系统军训口号,霸气的军训口号大全