最近在读一本进阶的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》整理(一)——作用域、提升与闭包的更多相关文章

  1. javascript深入浅出图解作用域链和闭包

    一.概要 对于闭包的定义(红宝书P178):闭包就是指有权访问另外一个函数的作用域中的变量的函数. 关键点: 1.闭包是一个函数 2.能够访问另外一个函数作用域中的变量 文章首发地址于sau交流学习社 ...

  2. 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?

    在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...

  3. JavaScript作用域闭包(你不知道的JavaScript)

    JavaScript闭包.是JS开发project师必须深入了解的知识. 3月份自己曾撰写博客<JavaScript闭包>.博客中仅仅是简单阐述了闭包的工作过程和列举了几个演示样例,并没有 ...

  4. 《你不知道的JavaScript》第一部分:作用域和闭包

    第1章 作用域是什么 抛出问题:程序中的变量存储在哪里?程序需要时,如何找到它们? 设计 作用域 的目的:为了更好地存储和访问变量. 作用域:根据名称查找变量的一套规则,用于确定在何处以及如何查找变量 ...

  5. 你不知道的JavaScript(作用域和闭包)

    作用域和闭包 ・作用域 引擎:从头到尾负责整个JavaScript的编译及执行过程. 编译器:负责语法分析及代码生成等. 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非 ...

  6. 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)

    github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...

  7. 你不知道的JavaScript(上)作用域与闭包

    第一部分 作用域与闭包 第一章 作用域是什么 1.作用域 变量赋值操作会执行两个动作:首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后会在运行时引擎会在作用域中查找该变量,找到就会 ...

  8. 《你不知道的JavaScript(上)》笔记——作用域闭包

    当函数可以记住并访问所在的词法作用域时, 就产生了闭包, 即使函数是在当前词法作用域之外执行. function wait(message) { setTimeout( function timer( ...

  9. 读《你不知道的JavaScript(上卷)》后感-浅谈JavaScript作用域(一)

    原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们,也入手看看这 ...

随机推荐

  1. webpack之傻瓜式教程

    接触webpack也有挺长一段时间了,公司的项目也是一直用着webpack在打包处理,但前几天在教新人的情况下,遇到了一个问题,那就是:尽管网上的webpack教程满天飞,但是却很难找到一个能让新人快 ...

  2. 谈谈一些有趣的CSS题目(三)-- 层叠顺序与堆栈上下文知多少

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

  3. java: web应用中不经意的内存泄露

    前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...

  4. MIP改造常见问题二十问

    在MIP推出后,我们收到了很多站长的疑问和顾虑.我们将所有疑问和顾虑归纳为以下二十个问题,希望对大家理解 MIP 有帮助. 1.MIP 化后对其他搜索引擎抓取收录以及 SEO 的影响如何? 答:在原页 ...

  5. 【SQLServer】【恢复挂起的解决方案】附加文件时候的提示“无法重新生成日志,原因是数据库关闭时存在打开的事务/用户,该数据库没有检查点或者该数据库是只读的。 ”【数据库恢复】

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 先贴错误: 吐槽一下: 进入正题: 新建一个同名数据库 停止MSSQL服务 替换数据库文 ...

  6. ASP.NET Core应用中如何记录和查看日志

    日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.Logger ...

  7. JavaScript之职责链模式

    一.概述 职责链模式(Chain of responsibility),就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有 ...

  8. 按照Enterprise Integration Pattern搭建服务系统

    在前一篇文章中,我们已经对Enterprise Integration Pattern中所包含的各个组成进行了简单地介绍.限于篇幅(20页Word以内),我并没有深入地讨论各个组成.但是如果要真正地按 ...

  9. .NET基础 一步步 一幕幕[循环、逻辑语句块]

    循环.逻辑语句块   好久不写博客了,断更了好几天了,从上周五到今天,从北京到上海,跨越了1213.0公里,从一个熟悉的城市到陌生的城市,还好本人适应力比较好,还有感谢小伙伴的接风咯,一切都不是事,好 ...

  10. Vue2.0用components替换render报错

    怀疑是webpack配置的问题,改了一下午也没弄好.去群里问了一轮,也没个解决的. 在研究的过程中,发现了一篇好的讨论帖,看这个帖子能学到不少东西.暂时放弃这个问题的研究了,太费时间,要深入学习编译原 ...