javaScript深入浅出之理解闭包
javaScript深入浅出之理解闭包
引言
闭包是个老生长谈的话题了,对于闭包网上也有很多不同的看法
- 《你不知道的javaScript》对于闭包是这么定义的:函数创建和函数执行不在同一个作用域下就会形成闭包。
- MDN对于闭包的定义为:闭包是指那些能够访问自由变量的函数。
- 《JS高级程序设计-第3版》对于闭包的定义: 闭包是指有权访问另外一个函数作用域中的变量的函数
好,现在你不用着急去搞明白它们的意思,我们先来讲下自由变量这个是个什么东西。
自由变量
自由变量:是指在函数中使用的变量即不是函数参数arguments
也不是函数内部声明的变量,那这个变量就是自由变量。
代码实例
function outer(){
var public = '前端自学驿站:';
function inner(name){
// 这个函数中用到了两个变量
public: 不是自己的函数,也不是函数内部定义的变量
name: 函数自己的参数
console.log(public + name); //前端自学驿站:北歌
}
return inner('北歌'); // 返回inner函数执行的返回值
}
outer();
上面这段代码中中public
不是自己的参数也不是函数内部声明的变量,那它就是一个自由变量
动动脑来分析下,思考下上面代码有形成闭包吗?
举例
上面的代码分析出来了吗? 现在我们再来看个例子,对比分析下闭包究竟是个什么东西。
function fun() {
var count = 1;
return function () {
count++; // 自由变量
console.log(count);
}
}
var fun2 = fun(); // fun2 => 返回的匿名函数
fun2(); // 函数在全局作用域调用,创建的时候是在fun作用域中创建的
简要的执行过程如下:
- 1、进入全局代码,全局调用栈执行,全局上下文初始化
- 2、
fun
调用,创建fun函数执行上下文,fun执行上下文被压入执行上下文栈 - 3、
fun
执行上下文初始化, 形成AO(Active Object)
活动对象,分析函数内的变量,参数, 确定this
的指向 - 4、
fun
进入执行完毕返回匿名函数给fun2
- 5、
fun2
调用, 按照2、3的步骤走一遍。
下面让我们来看看chrome控制台
上面截图已经把所要知道的标识了,关于chrome的调式这里就不多讲了,不懂的可以回过头去看js基础大纲
一步步调式下去,在调用fun2
函数的时候形成了闭包(Closure),从控制台可以看到(这是chrome浏览器为了开发人员好调式给我们搞出来的,可以把闭包认为是一个容器,存储的是键值对形式的值)。
所以说上面的代码形成了闭包,让我们来看看它是不是符合上面对于闭包的定义
- 《你不知道的javaScript》:函数创建和函数执行不在同一个作用域下就会形成闭包。
- MDN:闭包是指那些能够访问自由变量的函数。
- 《JS高级程序设计-第3版》闭包是指有权访问另外一个函数作用域中的变量的函数
对照上面的代码我们不难发现上面三个条件都符合
现在我们回过头来看看最前面的代码,它是闭包嘛?不是!
让我们来看看他为什么不是
function outer(){
var public = '前端自学驿站:';
function inner(name){
// 这个函数中用到了两个变量
public: 不是自己的函数,也不是函数内部定义的变量
name: 函数自己的参数
console.log(public + name); //前端自学驿站:北歌
return {
a: 1
}
}
return inner('北歌'); // 返回inner函数执行的返回值
}
let r = outer();
console.log(r)
console.log(r)
// 1.函数创建和函数执行是在同一个作用域下 (不符合)
// 2.引用了自由变量 (符合)
// 3.访问了另外一个函数作用域中的变量 (符合)
认为是的伙伴会说,从chrome控制台调式时inner
函数调用时确实形成了短暂的闭包,但是在函数执行完成时外层函数
没有东西被占用,outer函数执行的栈内存就被销毁了,所以从确切来说这不算是一个闭包。
总结
上面介绍的是实践角度,对于闭包的有多种介绍且说法不一。
按照的我理解闭包得符合两个条件:
1、函数执行完毕,函数内部创建的东西被函数外部引用了,形成不销毁的栈内存
2、在代码中引用了自由变量
闭包面试题
好了,现在让我们一起来做常见的闭包相关的面试题看看你有没有正真撑握它!
function fun(n, o) {
console.log(o);
return {
fun: function (m) {
return fun(m, n);
}
};
}
var a = fun(0); // ?
a.fun(1); // ?
a.fun(2); // ?
a.fun(3); // ?
var b = fun(0).fun(1).fun(2).fun(3); // ?
var c = fun(0).fun(1); // ?
c.fun(2); // ?
c.fun(3); // ?
解析
为了看的方便建议将题目复制到
IDE
中跟解析对照着看,这样更易于理解。
// 为了能够解析方便, 我将代码做下拆分,以下是上半部分
function fun(n, o) {
/**
* 第一次执行fun
* n: 0
* o: undefined
*/
/**
* 第二次执行:a.fun(1)中调用的fun(1, 0)
* n:1
* o: 0
*/
/**
* 第三次执行:a.fun(2)中调用的fun(2, 0)
* n:2
* o: 0
*/
/**
* 第四次执行:a.fun(3)中调用的fun(3, 0)
* n:3
* o: 0
*/
console.log(o);
return {
fun: function (m) {
/**
* 第一次调用: m: 1
* fun(1, n是自由变量用的是上层作用域中的0)
*/
/**
* 第二次调用: m: 2
* fun(2, n是自由变量用的是上层作用域中的0)
*/
/**
* 第二次调用: m: 3
* fun(3, n是自由变量用的是上层作用域中的0)
*/
return fun(m, n);
}
};
}
var a = fun(0); // undefined 执行返回了一个对象被a引用了,形成闭包fun中的变量没有被销毁
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0
// => 下半部分,解析也是只写下半部分的
function fun(n, o) {
/**
* 第一次执行:fun(0)
* n: 0
* o: undefined
*/
/**
* 第二次执行:对象.fun(1)调用的fun(1, 0)
* n: 1
* o: 0
*/
/**
* 第三次执行:对象.fun(2)调用的fun(2, 1)
* n: 2
* o: 1
*/
/**
* 第四次次执行:对象.fun(3)调用的fun(3, 2)
* n: 3
* o: 2
*/
console.log(o);
return {
fun: function (m) {
/**
* 第一次调用:对象.fun(1)
* m: 1
* fun(1, n是用的是上层作用域中的0)
*/
/**
* 第二次调用:对象.fun(2)
* m: 2
* fun(2, n是用的是上层作用域中的1)
*/
/**
* 第二次调用:对象.fun(3)
* m: 3
* fun(3, n是用的是上层作用域中的2)
*/
return fun(m, n);
}
};
}
/* var a = fun(0); // undefined 执行返回了一个对象被a引用了,形成闭包fun中的变量没有被销毁
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0 */
// 第一次执行fun(0)返回的是对象,调用对象的中的fun方法
var b = fun(0).fun(1).fun(2).fun(3); // undefined, 0, 1, 2
// 能看懂上面的话剩下的这些应该就能自己分析出来了,答案也不给了分析好后直接控制台输出
var c = fun(0).fun(1); // ?
c.fun(2); // ?
c.fun(3); // ?
好了,解析到这就差不多不然代码篇幅太长了,只要把前面搞懂后面就完全没有问题。解析部分可以好好看看。
javaScript深入浅出之理解闭包的更多相关文章
- Javascript之深入理解闭包
闭包算是js里面比较不容易理解的点,尤其是对于没有编程基础的人来说. 其实闭包要注意的就那么几条,如果你都明白了那么征服它并不是什么难事儿.下面就让我们来谈一谈闭包的一些基本原理. 闭包的概念 一个闭 ...
- javascript之彻底理解闭包
闭包是函数和声明该函数的词法环境的组合. function init() { var name = "Mozilla"; // name 是一个被 init 创建的局部变量 fun ...
- 深入理解javascript函数参数与闭包(一)
在看此文章,希望先阅读关于函数基础内容 函数定义与函数作用域 的章节,因为这篇文章或多或少会涉及函数基础的内容,而基础内容,我放在函数定义函数作用域 章节. 本文直接赘述函数参数与闭包,若涉及相关知识 ...
- JavaScript——以简单的方式理解闭包
闭包,在一开始接触JavaScript的时候就听说过.首先明确一点,它理解起来确实不复杂,而且它也非常好用.那我们去理解闭包之前,要有什么基础呢?我个人认为最重要的便是作用域(lexical scop ...
- javascript深入理解闭包
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...
- javascript深入理解闭包(转)
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...
- JavaScript 从闭包可以做什么开始,将有助于理解闭包
本文内容 函数内部访问全局变量 函数外部不能直接访问局部变量 函数外部访问局部变量 保护私有成员 持久性 模块化 抽象性 闭包是 JavaScript 的重要特性,非常强大,可用于执行复杂的计算,可并 ...
- javascript花式理解闭包
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...
- JavaScript深入浅出-闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function (){ var localVal ...
随机推荐
- Spring基础之IOC
一.ioc能解决什么问题 1.Spring是什么 spring是以ioc和aop为核心,能整合第三方框架和类库的企业级应用开源框架. 2.程序的耦合问题 例子:Driver类必须存在,编译才通过,Jd ...
- jQuery——选择器效率
N1:$('#box').find('p'):最快,直接了当的找到对应的节点jQuery对象: N2:$('p','#box'):注意不是$('p,#box')!!!,jQuery会按照从右往左的顺序 ...
- 10 个提高效率的 Linux 命令别名
在 Linux 环境下工作的工程师,一定会对那些繁琐的指令和参数命令行印象深刻吧.而且,可怕的不是繁琐,而是需要大量重复输入这些繁琐的命令. 在 Linux 下我们有个别名命令 alias ,可以将那 ...
- 附021.Traefik-ingress部署及使用
一 Helm部署 1.1 获取资源 [root@master01 ~]# mkdir ingress [root@master01 ~]# cd ingress/ [root@master01 ing ...
- 百度云百度网盘VIP不限速破解版绿色版-实测可用
百度云百度网盘不限速VIP破解版绿色版-下载地址:https://www.90pan.com/b1548999
- MD5安全吗?
MD5的破解方法目前分为两类:一类为彩虹表破解:一类为专业的MD5破解站点. 1.彩虹表 彩虹表是一个庞大的.针对各种可能的字母组合预先计算好的哈希值的集合.彩虹表不仅针对MD5算法,主流的算法都有对 ...
- 【HBase】 常用命令
创建表 hbase(main):006:0> create 'goods_zc','type' 0 row(s) in 1.3090 seconds => Hbase::Table - g ...
- Java实现 蓝桥杯VIP 基础练习 回形取数
问题描述 回形取数就是沿矩阵的边取数,若当前方向上无数可取或已经取过,则左转90度.一开始位于矩阵左上角,方向向下. 输入格式 输入第一行是两个不超过200的正整数m, n,表示矩阵的行和列.接下来m ...
- Java实现 蓝桥杯VIP 算法训练 s01串
题目描述 s01串初始为" 0" 按以下方式变换 0变1,1变01 数据规模和约定 0~19 输入 1个整数(0~19) 输出 n次变换后s01串 样例输入 3 样例输出 101 ...
- Java实现 LeetCode 374 猜数字大小
374. 猜数字大小 我们正在玩一个猜数字游戏. 游戏规则如下: 我从 1 到 n 选择一个数字. 你需要猜我选择了哪个数字. 每次你猜错了,我会告诉你这个数字是大了还是小了. 你调用一个预先定义好的 ...