JavaScript闭包。是JS开发project师必须深入了解的知识。

3月份自己曾撰写博客《JavaScript闭包》。博客中仅仅是简单阐述了闭包的工作过程和列举了几个演示样例,并没有去刨根问底。将其弄明确。

如今随着对JavaScript更深入的了解,也刚读完《你不知道的JavaScript(上卷)》这本书,所以乘机整理下。从底层和原理上去刨一下。

JavaScript并不具有动态作用域,它仅仅有词法作用域。

词法作用域是在写代码或者说定义时确定的。而动态作用域是在运行时确定的。

了解闭包前。首先我们得知道什么是词法作用域(作用域是由书写代码时函数声明的位置来决定的)。

一、何为闭包

演示样例1:

function foo(){
var a = 2;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz(); //2

在foo()运行后,通常觉得垃圾回收机制会将foo()的整个内部作用域都被销毁;而闭包能够阻止这样事情发生,让其内部作用域依旧存在。

由于bar()处于foo()内部。它拥有涵盖foo()作用域的闭包,使得该作用域能够一直存活,以供bar()在之后不论什么时间进行引用。

bar()依旧持有对该作用域的引用,而这个引用就叫作闭包
简言之:当函数能够记住并訪问所在的词法作用域,即使函数是在当前词法作用域之外运行,这时就产生了闭包。

演示样例2:

不管使用何种方式对函数类型的值进行传递,当函数在别处被调用时都能够观察到闭包。

function foo(){
var a = 2;
function baz(){
console.log(a);
}
bar(baz);
} function bar(fn){
fn(); // 这就是闭包
}

演示样例3:

将一个内部函数(timer)传递给setTimeout。timer具有涵盖wait()作用域的闭包,保有对变量message的引用。

wait()运行1000毫秒后,它的作用域并不会消失,timer依旧保有wait()作用域的闭包。

function wait(message){
setTimeout( function timer(){
console.log(message);
},1000);
}
wait("Hello,ligang");

演示样例4:

下述activator()具有涵盖setupBot()作用域的闭包。

function setupBot(name, selector){
$(selector).click(function activator(){
console.log("Activating: "+ name);
});
}
setupBot("Closure Bot 1", "#bot_1");
setupBot("Closure Bot 2", "#bot_2");

二、循环和闭包

for(var i=1; i<=5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}
// 期望:每秒一次的频率输出1~5
// 结果:每秒一次的频率输出五次6

先解释一下:“i*1000”,5个定时分别在1s、2s、3s、4s、5s后运行,并非1s、3s、6s、10s、15s。

也就是频率为1s,不是每次间隔添加1s。假设去掉i写成“1000”,会在for运行完1s后直接输出五次6。

回调函数在循环结束后才被运行,因此输出的是循环终止条件是i值。其实。当定时器运行时即使每一个迭代中运行的是setTimeout(..., 0)。全部的回调函数依旧是在循环结束后才被运行。

依据作用域的工作原理,虽然五个函数是在各个迭代中分别定义的。可是它们都被封闭在一个共享的全局作用域中,因此实际上仅仅有一个i。

解决方式1:

for(var i=0; i<=5; i++){
(function(j){
setTimeout(function timer(){
console.log(j);
}, j*1000 );
})(i);
}
// 结果:每秒一次的频率输出1~5

每一个迭代都生成一个新的作用域,使得延迟函数的回调能够将新的作用封闭在每一个迭代内部,每一个迭代中都会含有一个具有正确值的变量供我们訪问。

解决方式2(ES6):

for(var i=0; i<=5; i++){
let j = i;
setTimeout(function timer(){
console.log(j);
}, j*1000 );
}
// 结果:每秒一次的频率输出1~5
for(let i=0; i<=5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000 );
}
// 结果:每秒一次的频率输出五次6

三、模块

模块须要具备两个必要条件:

(1)必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。

(2)封闭函数必须返回至少一个内部函数,这样内部函数才干在私有作用域中形成闭包,而且能够訪问或者改动私有的状态。

典型的模块化:

function CoolMoudle(){
var something = "cool";
var doSomething = function(){
console.log(something);
}
return{
doSomething: doSomething
};
}
var foo = CoolMoudle(); //假设不运行外部函数CoolMoudle(),内部作用域和闭包都无法创建
foo.doSomething(); //cool

单例模式:

var foo = (function CoolModule(id){
function change(){
// 改动公共API
publicAPI.identify = identify2;
}
function identify1(){
console.log(id);
}
function identify2(){
console.log(id.toUpperCase());
}
var publicAPI = {
change: change,
identify: identify1
};
return publicAPI;
})("foo module");
foo.identify(); //foo module
foo.change();
foo.identify(); //FOO MODULE

JavaScript作用域闭包(你不知道的JavaScript)的更多相关文章

  1. JavaScript作用域闭包简述

    JavaScript作用域闭包简述 作用域 技术一般水平有限,有什么错的地方,望大家指正. 作用域就是变量起作用的范围.作用域包括全局作用域,函数作用域以块级作用域,ES6中的let和const可以形 ...

  2. JS闭包—你不知道的JavaScript上卷读书笔记(二)

    关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...

  3. 为JavaScript正名--读你不知道的JavaScript(持续更新..)

    你不知道的JavaScript上卷 JavaScript和Java的关系就像Carnival和Car的关系一样,八竿子打不着. JavaScript易上手,但由于其本身的特殊性,相比其他语言能真正掌握 ...

  4. Python自动化 【第十六篇】:JavaScript作用域和Dom收尾

    本节内容: javascript作用域 DOM收尾 JavaScript作用域 JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走 ...

  5. 《你不知道的JavaScript》整理(一)——作用域、提升与闭包

    最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,里面分析了很多基础性的概念. 可以更全面深入的理解JavaScript深层面的知识点. 一.函数作用域 ...

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

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

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

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

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

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

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

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

随机推荐

  1. CentOS下安装SVN服务端

    ---恢复内容开始--- 1.使用yum安装 yum install subversion 2.创建仓库 1.创建成功后在svn下面多了几个文件夹. cd /home mkdir svn svnadm ...

  2. tload---显示系统负载

    tload命令以图形化的方式输出当前系统的平均负载到指定的终端.假设不给予终端机编号,则会在执行tload指令的终端机显示负载情形. 语法 tload(选项)(参数) 选项 -s:指定闲时的刻度: - ...

  3. js闭包实例展示

    准确来说,闭包是基于正常的垃圾回收处理机制下的.也就是说,一般情况一个函数(函数作用域)执行完毕,里面声明的变量会全部释放,被垃圾回收器回收.但闭包利用一个技巧,让作用域里面的变量,在函数执行完之后依 ...

  4. YOLOv3学习笔记

    Darknet/Yolo官网:https://pjreddie.com/darknet/yolo/ GitHub程序:https://github.com/AlexeyAB/darknet 关于程序程 ...

  5. 紫书 例题 10-18 UVa 11346(连续概率)

    就是面积计算,没什么好说的. #include<cstdio> #include<cmath> #define REP(i, a, b) for(int i = (a); i ...

  6. FZU 1202 信与信封问题 二分图匹配

    找匹配中的关键边. 做法: 拆掉一条匹配边,然后对边两边的点做一次增广,如果可以增广,那么此边不是关键边,否则是关键边. 详情可以参见:http://www.docin.com/p-109868135 ...

  7. 【Round #36 (Div. 2 only) B】Safe Spots

    [题目链接]:https://csacademy.com/contest/round-36/task/safe-spots/ [题意] 给你n个数字构成的序列; 每个位置上的数都由0和1组成; 对于每 ...

  8. Hadoop RPC框架

    1.RPC框架概述 1.1 RPC(Remote Procedure Call Protocol)--远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的协议. R ...

  9. 基于BP神经网络的简单字符识别算法自小结(C语言版)

    本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前面的闲话: 自我感觉自己应该不是一个非常 ...

  10. 创建带有IN类型参数的存储过程(四十八)

    创建带有IN类型参数的存储过程 我们经常要从数据表中删除记录,一般情况我们删除记录都是根据id来删除的,比如我们通常要输入DELETE FROM 表名 WHERE 后面跟上我们的条件,因为我们要经常写 ...