var 是函数级作用域或者全局作用域,let是块级作用域
看一个例子
function foo() {
for (var index = 0; index < array.length; index++) {
//..循环中的逻辑代码
}
console.log(index);//=>5
}
foo()
console.log(index)//Uncaught ReferenceError: index is not defined
foo函数下的index输出5,全局下的index不存在
现在我们把var 换为let
function foo() {
for (let index = 0; index < array.length; index++) {
//..循环中的逻辑代码
}
console.log(index)//Uncaught ReferenceError: index is not defined
}
foo()
报错了,index不在foo函数作用域下,当然肯定也不会再全局下
因为var和let的这个区别(当然var和let的区别不止于此)所以导致了下面的这个问题
关于var的
const array = [1, 2, 3, 4, 5]
function foo() {
for (var index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(index);
}, 1000);
}
}
foo()
关于let的
const array = [1, 2, 3, 4, 5]
function foo() {
for (let index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(index);
}, 1000);
}
}
foo()
因为var和let 在作用域上的差别,所以到这了上面的问题
使用var 定义变量的时候,作用域是在foo函数下,在for循环外部,在整个循环中是全局的,每一次的循环实际上是为index赋值,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5;
let的作用局的块级作用局,index的作用域在for循环内部,即每次循环的index的作用域就是本次循环,下一次循环重新定义变量index;所以index每次循环的输出都不同
这里还有另外一个问题,setTimeout,这是一个异步,这就是我们今天要讨论的
setTimeout(func,time)是在time(毫秒单位)时间后执行func函数。浏览器引擎按顺序执行程序,遇到setTimeout会将func函数放到执行队列中,等到主程序执行完毕之后,才开始从执行队列(队列中可能有多个待执行的func函数)中按照time延时时间的先后顺序取出来func并执行。即使time=0,也会等主程序运行完之后,才会执行。
上面的let是循环打印了12345,但是不是间隔1s打印的,是在foo函数执行1s后,同时打印的
function foo(){
let index = 0;
const array = [1, 2, 3, 4, 5]
const t = setInterval(()=>{
if (index < array.length) {
console.log(array[index]);
}
index++;
}, 1000);
if (index >= array.length) {
clearInterval(t);
}
}
foo()
我们上面说到,当for循环遇到了var,变量index的作用域在foo函数下,循环一次赋值一次,5次循环完成,index最后的结果赋值就为5;就是被最终赋值的index,就是5;
代码执行顺序是,先同步执行for循环,再执行异步队列,在for循环执行完毕后,异步队列开始执行之前,index经过for循环的处理,变成了5。
所以我们引入一个全局变量j,使j在for循环执行完毕后,异步队列开始执行之前,依然是0,在异步执行时进行累加
var j = 0;
for (var index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(j);
j++;
}, 1000 * index)
}
const array = [1, 2, 3, 4, 5]
function foo() {
for (let index = 0; index < array.length; index++) {
setTimeout(() => {
console.log(index);
}, 1000*index);
}
}
foo()
开始讨论方式四之前我推荐先阅读一遍我之前写过一篇文章
谈一谈javascript作用域
我们对上面的问题再次分析,for循环同步执行,在for循环内部遇到了setTimeout,setTimeout是异步执行的,所以加入了异步队列,当同步的for循环执行完毕后,再去执行异步队列,setTimeout中有唯一的一个参数数index
方式三可行,是因为let是块级作用域,每次for执行都会创建新的变量index,for循环执行完毕后,异步执行之前,创建了5个独立的作用域,5个index变量,分别是0,1,2,3,4,相互独立,互不影响,输出了预期的结果
如果说每次循环都会生成一个独立的作用域用来保存index,问题就会得到解决,所以,我们通过闭包来实现
const array = [1, 2, 3, 4, 5]
function foo() {
for (var index = 0; index < array.length; index++) {
function fun(j) {
setTimeout(function () {
console.log(j);
}, 1000 * j);
}
fun(index)
}
}
foo()
setTimeout中的匿名回调函数中引用了函数fun中的局部变量j,所以当fun执行完毕后,变量j不会被释放,这就形成了闭包
当然我们可以对此进行一下优化
const array = [1, 2, 3, 4, 5]
function foo() {
for (var index = 0; index < array.length; index++) {
(function(j) {
setTimeout(function () {
console.log(j);
}, 1000 * j);
})(index)
}
}
foo()
将foo函数改为匿名的立即执行函数,结果是相同的
for循环本身是同步执行的,当在for循环中遇到了异步逻辑,异步就会进入异步队列,当for循环执行结束后,才会执行异步队列
当异步函数依赖于for循环中的索引时(一定是存在依赖关系的,不然不会再循环中调动异步函数)要考虑作用域的问题,
在ES6中使用let是最佳的选择,
当使用var时,可以考虑再引入一个索引来替代for循环中的索引,新的索引逻辑要在异步中处理
也可以使用闭包,模拟实现let
在实际开发过程中,循环调用异步函数,比demo要复杂,可能还会出现if和else判断等逻辑,具体的我们下次再续
参考
通过for循环每隔两秒按顺序打印出arr中的数字
setTimeOut和闭包
《你不知道的JavaScript》上卷
闭包(closure)是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠...
使用闭包的方式实现一个累加函数 addNum,参数为 number 类型,每次返回的结果 = 上一次计算的值 + 传入的值
JavaScript 变量可以是局部变量或全局变量。私有变量可以用到闭包。闭包就是将函数内部和函数外部连接起来的一座桥梁。函数的闭包使用场景:比如我们想要一个函数来执行计数功能。
这篇文章主要介绍了JavaScript的闭包机制,针对内嵌函数的变量访问等问题分析了JS的闭包,写的十分的全面细致,具有一定的参考价值,JavaScript 变量可以是局部变量或全局变量。 私有变量可以用到闭包。
变量作用域:一个变量的作用域是程序源代码中定义这个变量的区域,在函数内声明的变量是局部变量,它只在该函数及其嵌套作用域里可见(js 函数可嵌套定义);
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另一个函数。来看下面的示例:
使用闭包能够让局部变量模拟全局变量一样,但是它只能被特定函数使用。我们都知道:1.全局变量可能会造成命名冲突,使用闭包不用担心这个问题,因为它是私有化,加强了封装性,这样保护变量的安全
在学习闭包之前,你必须清楚知道JS中变量的作用域。JS中变量的作用域无非就全局变量和局部变量,两者之间的关系是函数内部可以直接访问全局变量,但是函数外部是无法读取函数内部的局部变量的
JavaScript的运行机制:(1)所有同步任务都在主线程上执行,形成一个执行栈。(2)主线程之外,还有一个“任务队列”,只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
《JavaScript权威指南》:函数对象可以通过作用域链相互关联起来,函数体内部的变量可以保存在函数作用域内,这种特性称为“闭包”。不好理解?那就通俗点讲:所谓闭包,就是一个函数,这个函数能够访问其他函数的作用域中的变量。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!