let和var在for循环中的表现
来源:互联网 发布:mac系统怎么隐藏文件夹 编辑:程序博客网 时间:2024/05/16 18:36
var变量声明关键字
在ECMAScript3中,声明变量的关键字var用来在当前作用域中声明一个变量,作用域为函数作用域。
函数作用域:
// 函数function a(){ var i = 0; console.log(i) // i = 0;}console.log(i) // i = undefined// 块{ var j = 0; console.log(j) // j = 0}console.log(j) // j = 0;
上面可以看见,在函数中var声明的变量在函数外面是访问不到的,而在块中通过var声明的变量在块外面是能访问的,这就是说var是函数作用域,只有函数能约束他的作用范围,而代码块是做不到约束var定义变量的范围的。所以下面两个段代码效果是一样的
{ var j = 0; console.log(j) // j = 0}console.log(j) // j = 0// 根据变量提升原则可以改为下面的式子var j;{ j = 0; console.log(j) // j = 0;}console.log(j) // j = 0;
上面两段代码可以看出,在代码块中通过var定义的变量其实就是定义在块所在作用域的变量。
let变量声明关键字
let声明的变量和var声明的变量就作用域问题就是,let声明的变量是块级作用域;
块级作用域:
// 和var声明变量的例子比较{ let i = 0; console.log(i) // i = 0;}console.log(i) // error
上面例子可以看出let和var的不同点,var在代码块中定义的变量是代码块所在作用域的活动对象上面创建,而let是在代码块本身这个活动对象上面创建的变量。也就是说let声明的变量会被代码块所约束。当然,在函数中,函数体的代码也是一个代码块包含的,所以函数作用域任然在,或者说函数就是代码块的作用域约束的。
他们在for循环中的表现呢。
首先我们知道在for循环中,异步打印循环变量得到的值是一样的,都是循环最后一次的i的值
for(var i = 0;i<10;i++){ setTimeout(function(){ console.log(i) },100)} // 输出全是10// 输出全是10的原因是因为i是全局变量,最后访问的都是全局变量i,而每次循环改变i的值就是改变全局变量的值,故而输出值均为10// 之前对于这种问题的解决办法是通过闭包来实现for(var i = 0;i<10;i++){ (function(){ var j = i; setTimeout(function(){ console.log(j); },100) }())} // 输出0123456789;
上面解决办法的闭包实现,其实就是通过函数来构建一个作用域,每个作用域存储不同的i值,然后异步调用的函数是通过作用域链的调用规则访问到他创建时所在作用域的变量,就是创建时j的值,而j的值是局部变量,所以就能打印出0123456789;
而let呢,因为他是块级作用域,在for循环中,很明显我们是能找到一个{}
构成的代码块的
for(let i = 0;i<10;i++){ setTimeout(function(){ console.log(i) },100)} // 0123456789;
为什么使用let就成了这个样子呢,就是和let的块级作用域。解释就很简单了,因为每次循环都创建一个块级作用域,并且存上i的值,这里面的let定义的i
值就是局部变量,所以每次循环改变的就是对局部变量赋值,访问也是根据作用域链规则访问局部变量i
这样就得到了最后的结果。
同样的,还有一个例子就是当let定义在for循环之外呢
let i = 0;for(;i<10;i++){ setTimeout(function(){ console.log(i) },100)} // 全是10;
不是说好let是块级作用域么,为什么又是一个全是10,因为这里let定义的地方就是全局作用域下啊,所以这里的i
就是全局作用域,所以代码块对i
就没有约束能力了,因为i
是个全局变量,局部改变它的值就是改变全局变量的值,最后打印的i
也是访问全局变量i
打印其值,自然都是打印的同一个值。
举个例子
如果上面还没看懂,这里用多次自执行函数来模拟一下简单的for循环,比较let和var变量
// 类似简单for循环每次给循环给定义一个i并且赋值,因为var变量在块中并没有作用域约束,所以就是定义全局变量i,而重复声明一个同名变量,后面声明覆盖前面声明,所以最后i = 2,最后打印的访问的变量在局部找不到就向上级查找,找到全局变量i,均为2;{ var i = 0; setTimeout(function(){console.log(i)},100) // i = 2}{ var i = 1; setTimeout(function(){console.log(i)},100) // i = 2}{ var i = 2; setTimeout(function(){console.log(i)},100) // i = 2}
// 因为let有块级约束,所以每一个代码块中的i都是局部变量,所以每次打印访问的局部变量均不相同,所以就是各自打印各自作用域链中的局部变量的i{ let i = 0; setTimeout(function(){console.log(i)},100) // i = 0}{ let i = 1; setTimeout(function(){console.log(i)},100) // i =1}{ let i = 2; setTimeout(function(){console.log(i)},100) // i = 2}
// 和上面let定义变量做比较,因为var是函数作用域,这里用自执行函数来创建一个作用域来创建一个作用域与上面的let对比,最后结果一样。(function(){ var i = 0; setTimeout(function(){console.log(i)},100) // i = 0}());(function(){ var i = 1; setTimeout(function(){console.log(i)},100) // i = 1}());(function(){ var i = 2; setTimeout(function(){console.log(i)},100) // i = 2}())
// 同样,对于let定义在全局的情况下,看下面这个模拟。因为i是全局作用域,所以每次执行都是在给i重新赋值,当进程执行完毕之后,全局作用域的 i = 2;而最后打印中,因为局部并没有找到定义的i变量,所以顺着作用域链向上查找,找到了全局i = 2,所以最后打印的结果就是全为2let i = 0;{ i = 0; setTimeout(function(){console.log(i)},100) // i = 2}{ i = 1; setTimeout(function(){console.log(i)},100) // i = 2}{ i = 2; setTimeout(function(){console.log(i)},100) // i = 2}
// 同样,我们可以用var来模拟上面这种情况,依然是借助自执行函数来完成var i;(function(){ i = 0; setTimeout(function(){console.log(i)},100) // 2}());(function(){ i = 1; setTimeout(function(){console.log(i)},100) // 2}());(function(){ i = 2; setTimeout(function(){console.log(i)},100) // 2}());
关于作用域和作用域链
javascript作用域那些事
- let和var在for循环中的表现
- var和let定义变量在循环中的不同
- js中的let和var
- var和let在异步任务队列中的执行差异
- javascript中的var,let和const
- var let 和const
- let var和const
- let和var区别
- var和let区别
- JavaScript:for循环中的i,加上var
- JavaScript中的let与var
- JavaScript中的let与var
- var和let的区别
- JavaScript 之 var 和 let
- let和var的区别
- Swift 中的变量(var let , Optional)
- JavaScript ES6中的var、let、const
- Swift中的值类型和引用类型(let和var使用注意)
- 使用javascript进行表单验证
- 学习笔记
- 接收机结构之带通采样
- 蓝桥杯程序设计大赛真题及解析
- android 设置背景色,以及透明度
- let和var在for循环中的表现
- js动态创建div
- 关于Android系统目录下文件的操作
- JS 默认值
- CSS网站兼容性问题归类
- 第五届蓝桥杯切面条
- 异常与异常处理
- nyoj762~第k个互质数(容斥原理+二分)
- 第33课:彻底解密Spark 2.1.X中Shuffle 中Mapper端的源码实现