但是第二次我们再执行result()的时候就出现了问题,为什么会是2呢?按照流程,首先再foo2()函数内部寻找count,没有然后到外层寻找,找到了count=0,这时候count+1应该为1才对.这里就涉及到闭包的问题了.
首先我们在原来的代码中加一个debugger,然后到谷歌浏览器右键检查,点击resources就可以看到右边有一个Closure,浏览器的可视化已经证实了这的确是一个闭包.并且count=1已经存储在了Closure之中.也就说明count=1没有被销毁,等下次在调用result()的时候count=2.
认识作用域
要学习Clusure必须了解JavaScript的作用域相关知识 作用域包括:
1.全局作用域
2.函数作用域
4.块级作用域(es6 新出,解决 var 问题, 新增 let, const)
var count = 100; //全局作用域
function foo1() {
var count = 0; //函数全局作用域
return count; //返回函数
}
if (count == 1) {
//块级作用域
console.log(count);
}
上面代码简单可以看出作用域分类,需要注意是,一个函数(function)也是块级作用域,简单来说,一般有 {}都可以算做是一个块级作用域.
作用域链
作用域里面嵌套作用域,就形成了作用域链. 外部作用域无法访问内部的作用域,看如下例子
function foo(){
var n=1
function foo2(){
var m=1
console.log(n) //1
}
foo2()
}
foo()
console.log(n) //err: n is not defined
上述代码中在全局中无法访问内部的n,但是在嵌套的内部foo2()可以访问外部的函数,这就是作用域产生的特殊效果.
明白了作用域链,我们再来看个例子(很有迷惑性,仔细看看哦):
var name = ‘Mike’; //第一次定义name
function showName() {
console.log(name); //输出 Mike 还是 Jay ?
}
function changeName() {
var name = ‘Jay’; //重新定义name
showName(); //调用showName()
}
changeName();
上面的例子你觉得输出的是什么呢?答案是Mike.在这里我们引出了一个新的概念,词法作用域作用域有两种模型:
词法作用域(静态):js查找是按照代码书写时候的位置来决定的,而不是按照调用时候位置
动态作用域:目前还有使用的有Perl,Bash (可以自行了解)
通过词法作用域的的规则我们可以再来分析一下
调用changeName()时,找到这个函数
定义var name = “Jay”
调用showName()
在changeName()里面查找是否有showName()这个方法,发现没有,向外层查找,找到了
调用console.log(name),在函数内部查找有没有name,没有,向外查找,找到了,name=“Mike”
输出Mike
闭包
了解了上面的知识之后,终于来到了闭包
闭包在两本书上的官方解释:
1.小"黄"书(你不知道的JavaScript): 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行.
2.红宝书(JavaScript高级程序设计): 闭包是指有权访问另一个 函数作用域中的变量的函数
非常抽象的一个概念,我自己的一个理解是:
当一个变量(就像上面的name)既不是该函数内部的局部变量,也不是该函数的参数,相对于作用域来说,就是一个自由变量(引用了外部变量),这样就会形成一个闭包.
怎么说呢?我们再来看看一开始我们使用的demo
let count = 500; //全局作用域
function foo1() {
let count = 0; //函数全局作用域
function foo2() {
let count2 = 1; //随便新增一个变量
// count++; 注释
debugger;
//console.log(count); 注释
//return count; 注释
}
return foo2; //返回函数
}
let result = foo1();
result(); //结果为1
result(); //结果为2
再次使用浏览器看看,这时我们就发现Closure已经消失了,这也就证实我说的,如果函数内部不调用外部的变量,就不会形成闭包.但是如果调用了外部变量,那么就会形成闭包. 这也就是说不是所有的函数嵌套函数都能形成闭包
最后我们再来看一个循环闭包的例子
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
debugger;
console.log(i); // 输出什么?
}, 1000);
计算机网络
-
HTTP 缓存
-
你知道 302 状态码是什么嘛?你平时浏览网页的过程中遇到过哪些 302 的场景?
-
HTTP 常用的请求方式,区别和用途?
-
HTTPS 是什么?具体流程
-
三次握手和四次挥手
-
你对 TCP 滑动窗口有了解嘛?
-
WebSocket与Ajax的区别
-
了解 WebSocket 嘛?
-
HTTP 如何实现长连接?在什么时候会超时?
-
TCP 如何保证有效传输及拥塞控制原理。
-
TCP 协议怎么保证可靠的,UDP 为什么不可靠?
算法
-
链表
-
字符串
-
数组问题
-
二叉树
-
排序算法
-
二分查找
-
动态规划
-
BFS
-
栈
-
DFS
-
回溯算法