《你不知道的JavaScript》整理(一)——作用域、提升与闭包
最近在读一本进阶的JavaScript的书《你不知道的JavaScript(上卷)》,里面分析了很多基础性的概念。
可以更全面深入的理解JavaScript深层面的知识点。
一、函数作用域
1)函数作用域
就是作用域在一个“Function”里,属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
function foo(a) {
var b = 2;
function bar() {
// ...
}
var c = 3;
}
bar(); // 失败
console.log( a, b, c ); // 三个全都失败
上面的“foo”函数内的几个标识符,放到函数外面访问就都会报错,查看源码。

2)立即执行函数表达式
在任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏”起来,外部作用域无法访问包装函数内部的任何内容。
例如上面的bar、a等几个标识符。这样能够保护变量不被污染。
在写插件的时候经常会用到立即执行函数表达式,为的就是保护里面的变量。
var a = 2;
(function foo() {
var a = 3;
console.log( a ); //
})();
console.log( a ); //
“foo”中第一个( )将函数变成表达式,第二个( )执行了这个函数。
有一个专用术语:IIFE,代表立即执行函数表达式(Immediately Invoked Function Expression);
1. 进阶用法是把它们当作函数调用并传递参数进去
(function IIFE( global ) {
var a = 3;
console.log( a ); //
console.log( global.a ); //
})( window );
2. 一种变化的用途是倒置代码的运行顺序,在CMD或AMD项目中被广泛使用。
(function IIFE(factory) {
factory( window );
})(function def( global ) {
var a = 3;
console.log( a ); //
console.log( global.a ); //
});
二、块作用域
JavaScript不支持块作用域。
for(var i=0; i<10; i++) {
console.log( i );
}
上面的代码中的“i”相当于下面的
var i;
for(i=0; i<10; i++) {
console.log( i );
}
但也有例外,“try/catch”,catch就是一个块作用域。
try{
undefined(); // 执行一个非法操作来强制制造一个异常
}
catch(err) {
console.log( err ); // 能够正常执行!
}
console.log( err ); // ReferenceError: err not found

ES6改变了现状,引入了新的let关键字,let关键字可以将变量绑定到所在的任意作用域中(通常是{ .. }内部)。换句话说,let为其声明的变量隐式地了所在的块作用域。
三、提升
函数作用域和块作用域的行为是一样的,可以总结为:任何声明在某个作用域内的变量,都将附属于这个作用域。
1)编译与执行
变量和函数的所有声明都会在任何代码被执行前首先被处理,可以看下面的代码事例。
a = 2;
var a;
console.log(a);//
这段代码等价于:
var a;//定义声明是在编译阶段进行
a = 2;//赋值声明会被留在原地等待执行阶段
console.log(a);
2)函数优先
函数会首先被提升,然后才是变量。
foo(); //
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
var foo函数表达式尽管出现在function foo()的声明之前,但它是重复的声明(因此被忽略了),因为函数声明会被提升到普通变量之前。
而上面的代码相当于:
function foo() {
console.log( 1 );
}
foo(); //
foo = function() {
console.log( 2 );
};
四、闭包
1)定义
当函数可以记住并访问所在的作用域时,就产生了闭包,即使函数是在当前作用域之外执行。
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 —— 这就是闭包的效果。
1. 将函数“bar”赋值给“baz”,执行“baz”,当前作用域并不在“bar”的作用域,但是可以执行。
2. 闭包还会阻止垃圾回收,当“foo”执行完后,内部作用域仍然存在。这样才能让“baz”执行。
2)将函数作为参数传递
function foo() {
var a = 2;
function baz() {
console.log( a ); //
}
bar( baz );
}
function bar(fn) {
fn(); //这就是闭包!
}
把内部函数baz传递给bar,当调用这个内部函数时(fn),它涵盖的foo()内部作用域的闭包就可以观察到了,因为它能够访问a。
如果将函数当作第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。
在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!
3)循环和闭包
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
每次打印出来都将会是6,延迟函数的回调会在循环结束时才执行,查看源码。
根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
现在用闭包来实现每次打印不同的i。
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j * 1000);
})(i);
}
IIFE会通过声明并立即执行一个函数来创建作用域。setTimeout中的回调可以记住当前的作用域,每个作用域中的参数“j”都是不同的。
《你不知道的JavaScript》整理(一)——作用域、提升与闭包的更多相关文章
- javascript深入浅出图解作用域链和闭包
一.概要 对于闭包的定义(红宝书P178):闭包就是指有权访问另外一个函数的作用域中的变量的函数. 关键点: 1.闭包是一个函数 2.能够访问另外一个函数作用域中的变量 文章首发地址于sau交流学习社 ...
- 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?
在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...
- JavaScript作用域闭包(你不知道的JavaScript)
JavaScript闭包.是JS开发project师必须深入了解的知识. 3月份自己曾撰写博客<JavaScript闭包>.博客中仅仅是简单阐述了闭包的工作过程和列举了几个演示样例,并没有 ...
- 《你不知道的JavaScript》第一部分:作用域和闭包
第1章 作用域是什么 抛出问题:程序中的变量存储在哪里?程序需要时,如何找到它们? 设计 作用域 的目的:为了更好地存储和访问变量. 作用域:根据名称查找变量的一套规则,用于确定在何处以及如何查找变量 ...
- 你不知道的JavaScript(作用域和闭包)
作用域和闭包 ・作用域 引擎:从头到尾负责整个JavaScript的编译及执行过程. 编译器:负责语法分析及代码生成等. 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非 ...
- 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)
github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...
- 你不知道的JavaScript(上)作用域与闭包
第一部分 作用域与闭包 第一章 作用域是什么 1.作用域 变量赋值操作会执行两个动作:首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后会在运行时引擎会在作用域中查找该变量,找到就会 ...
- 《你不知道的JavaScript(上)》笔记——作用域闭包
当函数可以记住并访问所在的词法作用域时, 就产生了闭包, 即使函数是在当前词法作用域之外执行. function wait(message) { setTimeout( function timer( ...
- 读《你不知道的JavaScript(上卷)》后感-浅谈JavaScript作用域(一)
原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们,也入手看看这 ...
随机推荐
- mysql每秒最多能插入多少条数据 ? 死磕性能压测
前段时间搞优化,最后瓶颈发现都在数据库单点上. 问DBA,给我的写入答案是在1W(机械硬盘)左右. 联想起前几天infoQ上一篇文章说他们最好的硬件写入速度在2W后也无法提高(SSD硬盘) 但这东西感 ...
- 对抗密码破解 —— Web 前端慢 Hash
(更新:https://www.cnblogs.com/index-html/p/frontend_kdf.html ) 0x00 前言 天下武功,唯快不破.但在密码学中则不同.算法越快,越容易破. ...
- RestTemplate发送请求并携带header信息
1.使用restTemplate的postForObject方法 注:目前没有发现发送携带header信息的getForObject方法. HttpHeaders headers = new Http ...
- .Net Core上也可以使用的二维码组件
我Fork了QRCoder,并且兼容了.Net Core,图形库用的是ZKWeb.System.Drawing Github: https://github.com/zkweb-framework/Q ...
- LoadRunner函数百科叒叒叒更新了!
首先要沉痛通知每周四固定栏目[学霸君]由于小编外派公干,本周暂停. 那么这周就由云层君来顶替了,当然要要说下自己做的内容啦,DuangDuang! <LoadRunner函数百科>更新通知 ...
- 后缀数组的倍增算法(Prefix Doubling)
后缀数组的倍增算法(Prefix Doubling) 文本内容除特殊注明外,均在知识共享署名-非商业性使用-相同方式共享 3.0协议下提供,附加条款亦可能应用. 最近在自学习BWT算法(Burrows ...
- java时间
Calendar.getInstance().getTime() 获取当前时间(包括星期和时区 CST China Standard Time): Fri Jan 06 21:03:36 CST 2 ...
- Spring获取ApplicationContext
在Spring+Struts+Hibernate中,有时需要使用到Spring上下文.项目启动时,会自动根据applicationContext配置文件初始化上下文,可以使用ApplicationCo ...
- CSS3自定义滚动条样式 -webkit-scrollbar(转)
有没有觉得浏览器自带的原始滚动条很不美观,同时也有看到很多网站的自定义滚动条显得高端,就连chrome32.0开发板都抛弃了原始的滚动条,美观多了.那webkit浏览器是如何自定义滚动条的呢? 前言 ...
- 如何使用本地账户"完整"安装 SharePoint Server 2010+解决“New-SPConfigurationDatabase : 无法连接到 SharePoint_Config 的 SQL Server 的数据 库 master。此数据库可能不存在,或当前用户没有连接权限。”
注:目前看到的解决本地账户完整安装SharePoint Server 2010的解决方案如下,但是,有但是的哦: 当我们选择了"完整"模式安装SharePointServer201 ...