-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Day15 - let为什么能够解决循环陷阱 #58
Comments
当var声明变量时,不存在块级作用域,a数组内每个函数中的i都是向上查询作用域a,所以结果都是10 var a = [];
for (var i = 0; i < 10; i++) {
// 作用域a
a[i] = function () {
// 作用域b
console.log(i);
};
}
a[6](); // 10 当let声明变量时,由于存在块级作用域,每次循环i都是一个新的变量,都会创建一个新的词法作用域,所以最后是6,我们可以通过babel把let代码编译成es5代码,如下所示: "use strict";
var a = [];
var _loop = function _loop(i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
a[6](); |
for (let i = 0; i < 16; i++) { |
因为let声明的范围是块作用域;每个花括号就是一个块作用域,每个块作用域的变量相隔离;而var 声明是函数作用域,var可以重复定义一个变量,覆盖之前的值; |
|
let声明的变量会形成块级作用域,每一次循环都是在一个块里面声明新的变量,于是就相互独立了。而 var 声明的变量会被提升到全局,所以每次循环都是用同一个变量,然后重新赋值。 |
回答let 通过块级作用域去解决,循环陷阱造成的原因是用 展开let 声明的变量仅在块级作用域生效, 所以 for 循环中,每个循环的 另外这个 |
|
es5不存在块级作用域,所以迭代变量i提升,然后对于a数组内每一个函数内的i都是向上查询作用域a的,所以结果是10 |
|
var声明的变量不会有块级作用域,该变量向上提升到全局作用域,所以执行的都是i最后的值 |
var 声明的变量会提升到全局作用域,所以执行的都是i最后的值 |
由于var声明不存在块级作用域,所以for循环中每次循环都会覆盖变量的值,而let则存在块级作用域,每次for循环中对于遍历的创建的函数都是独立的作用域,在没有let之前,解决这个问题需要创建函数作用域来解决 |
使用var会有问题是因为js没有块级作用域,只有函数作用域
let能解决这个问题是因为,es6中使用let,和const实现了块级作用域。
|
通过var声明的变量没有块级作用域。 var x = 1;
{
var x = 2;
}
console.log(x); // 输出 2 使用let和 const 相比之下,使用 let和const声明的变量是有块级作用域的。 let x = 1;
{
let x = 2;
}
console.log(x); // 输出 1 使用let声明的变量在块级作用域内能强制执行更新变量,下面的两个例子对比: var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {console.log(i);};
}
a[0](); // 10
a[1](); // 10
a[6](); // 10
/********************/
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {console.log(i);};
}
a[0](); // 0
a[1](); // 1
a[6](); // 6
```js |
ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。 也就是说let定义的变量只有块级作用域内有效 |
循环陷阱产生的原因是因为使用 var 声明的变量,是在当前的函数作用域或者是全局作用域生效的,所有的循环使用的都是同一个变量的值,所以会导致打印出来的值都是该变量最后的值 使用 let 能够解决循环陷阱是因为 let 产生了一个块级作用域,每个块级作用域都重新定义了一个变量,该变量只在当前块级作用域生效,每个块级作用域使用的都是不同的变量,因此不会产生循环陷阱 |
而 |
|
问题
可以看到当我们在 let 做了什么
|
var 声明的i会提升到当前函数的作用域,所以最后输出10 |
循环陷阱指的是用var声明循环变量: 这里会输出什么呢? 为什么会这样呢? 为什么用let能解决呢?
总结一下: |
let声明的变量只在其声明的块作用域有效,简单的说块级作用域就是一个{},在这里每一次进入for循环都会有一个独立的块级作用域{},所以只会输出当前块级作用域循环到的值,而不会再向上级作用域查找。 |
var 声明的变量在整个函数作用域内都有效 这也就导致如下两种不同的情况 function test(){
// var 声明的变量在当前函数作用域内都有效
for(var i=0;i<6;i++){
console.log(i)
}
console.log(i);// 6
}
test()
function test1(){
// let声明的变量只在循环块内才生效
for(let i=0;i<6;i++){
console.log(i)
}
console.log(i);// i is not defined
}
test1() let和const是如何支持块级作用域的呢? 在执行上下文中,扫描变量阶段 执行代码的代码的时候,当遇到变量,会先在当前词法环境内自顶向下寻找变量,如果没找到,再去变量环境寻找,当跳出当前块后,词法环境栈顶的所有变量弹出。这也就导致了let和const只会在所属词法环境对应的块级作用域内才有效。 这也就是let和const在整个代码编译和执行过程中的支持块级作用域的原理。 |
在ES5标准中,for循环都是通过var来声明变量,而在js中,var是没有独立的作用域的,变量的访问会从当前作用域开始,顺着作用域链向上查找,因此,使用var声明在循环中的作用域实际上是共用的,var所定义的变量i也是共用的,因此当在循环中使用i绑定事件,其在执行时,for循环早就执行完了,变量i也多次被赋值。 |
如果使用var声明变量进行for循环会有问题,是因为js没有块级作用域,只有函数作用域。 |
原来会有循环陷阱问题,是因为我们用了var关键字去声明变量,变量就会被放在块所在的函数的作用域里面,这样子的话,你N次循环访问到的其实都是同一个变量。 那么用了let之后,let声明的变量是放在块本地的本地的作用域里面的,那么你N次循环会有N个块,然后,他们会有各自的变量,是互相独立的,这样就不会有循环陷阱的问题。 |
大家都在说let是块作用域,每次循环都重新定义,我有一个疑问,for循环头中的let只在第一次循环开始前定义,后续每次都不会重新定义,这个“每次循环都重新定义”是如何得出的呢? update: ` a6 ` |
已收到你的邮件,谢谢!
|
var有陷阱
let无陷阱
The text was updated successfully, but these errors were encountered: