1、作用域
作用域就是可以访问的变量的范围。
ES5:全局用域、局部作用域(函数作用域);ES6:新增块级作用域。
注:在JS中所有没有被赋值的变量均为全局变量;在局部作用域定义的变量,只能在函数内部访问,当函数执行完之后,这个局部变量也相应的被销毁。
2、闭包概念
可以访问其他函数内部变量的函数,通常情况下,函数内部的变量是无法在函数外部无法访问的,因此使用闭包的作用就具备实现了能在外部访问某个函数内部变量的功能。
3、闭包产生的原因
作用域链:当访问一个变量时,代码解释器会首先在当前的作用域中查找,如果没有找到就去父级作用域去查找,直到找到该变量或者不存在父级作用域中。
闭包产生的本质就是:当前环境中存在指向父级作用域的引用。
问题1:是不是只有返回函数才算产生了闭包?
解:其实也不是,只需要让父级作用域的引用存在即可。
4、闭包的表现形式
1、返回函数;
2、作为函数参数传递
3、回调函数
4、立即执行函数
5、通过定时器循环输出自增的数字如何实现
for (var index = 0; index < 5; index++) {
setTimeout(() => {
console.log(index)
})
}
以上代码输出结果
5
5
5
5
5
为什么呢?
第一点:因为为宏任务,由于js中单线程eventLoop机制,在主线程执行完同步任务后才会去执行宏任务,因此setTimeout循环结束后才会依次执行setTimeout中的回调。
第二点:因为setTimeout函数也是一个闭包,往上找他的父级作用域就是window,变量i为window上的全局变量,开始执行setTimeout之前变量i已经就是5了,因此最后输出连续的都是5。
那如何顺序输出0到4呢?
方案一、通过立即执行函数,创建闭包
for (var index = 0; index < 5; index++) {
((i) => {
setTimeout(() => {
console.log(i)
})
})(index);
}
方案二、用let关键字定义变量
for (let index = 0; index < 5; index++) {
setTimeout(() => {
console.log(index)
})
}
方案三、传入setTimeout第三个参数(传递给函数指定的函数的附加参数)
for (let index = 0; index < 5; index++) {
setTimeout((i) => {
console.log(i)
},0,index)
}