-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
ES6 系列之 let 和 const #82
Comments
哈,还是先写了 ES6 ,手动 DOGE。 |
写的很不错
…On Mon, May 21, 2018 at 5:59 PM BeijiYang ***@***.***> wrote:
哈,还是先写了 ES6 ,手动 DOGE。
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AB212IEY7zdOT5R-HRXTU5ECl7wuAbZOks5t0pARgaJpZM4UGXxM>
.
|
期待V8源码系列已经很久了,我的大刀已经饥渴难耐了! |
for (var i = 0; i < 3; i++) {
var i = 'abc';
console.log(i);
}
// abc 这里为什么会只输出一个abc呢??求教 |
@brushbird |
非常赞。 |
@BeijiYang 写完 ES6 系列,等我写 React 系列的时候,就可以尽情的使用 ES6 的语法 (๑•̀ㅂ•́)و✧ |
@CharlyCheng 这个……不是想让你伤心……但我没有说过写 V8 系列…… |
@shadowprompt @thisisandy @horizon0514 @JChermy 感谢你们,送上致意 o(////▽////)q |
let 在 for 循环中创建的隐藏作用域,以及babel模拟创建函数作用域,用以保存i的值,让我印象深刻呀。以前只知道for循环中let可以解决var变量问题,现在知道原理了 |
我就是来刷刷存在感滴 |
@brushbird 'abc'<3 -> false |
博主有个问题不太理解~ |
@lzy68187311 你确定编译后是这个样子?应该不是吧 |
@heyunjiang ( ̄▽ ̄)~* |
@liuxinqiong ( ̄∇ ̄) |
有两个疑问: // 例子1 这个例子中,自执行函数中 |
@JChermy me, jd sz, too. 同事呀 |
应该为 |
有两个疑问: // 例子1 这个例子中,自执行函数中 |
@mqyqingfeng ,js 深入系列时,有人要说分析V8源码来,好像后来再看,已经从入门到放弃了 |
@CharlyCheng js引擎会先扫描整个代码,在自执行函数作用域内扫描到了变量定义(let value = 'local'),导致value被加入死区。之后执行过程中在函数作用域内访问value,则报错 |
不要V8,但说好的React系列呢。。 |
我的项目babel编译下面这段代码也有问题 // 编译前
for (let i = 0; i<3;i++) {
let i = 10
console.log(i) //10,10,10
}
// 编译后
for (var i = 0; i < 3; i++) {
var i = 10;
console.log(i); //10
} 全局安装的babel-cli@6.26.0,以及项目下安装的babel-preset-env@^1.7.0, babel-preset-stage-2@^6.24.1 //.babelrc
{
"presets": [
["env", {
"targets": {
"browsers": ["last 3 versions", "> 2%", "ie >= 9", "Firefox >= 30", "Chrome >= 30"]
},
"modules": false,
"loose": true,
"useBuiltIns": true
}],
"stage-2"
]
}
有人遇到和我一样的问题么 |
babel处理for中的let的方法,可以说是一种非常优雅的解决办法了 小鸡蛋里挑骨头: |
nice 马飞 |
大佬 就是我遇到一个解构赋值的地方想请教一下
1 和 2+3有什么区别呢,感觉看起来一样,但是答案似乎有出入... |
感谢楼主 |
我也是这样想的,但是当执行 |
当执行完 |
js里面的所有声明都是有提升的,也是看过了您的关于变量对象的文章才得出此结论,提升的本质应该是当前变量在变量对象初始化时,创建于变量对象中。把变量对象的变化过程分为三个步骤,第一步是创建,第二步是初始化,第三步是赋值。对于var/class/function/形参等创建和初始化在变量对象的初始化阶段,对于function、class、形参等赋值也在变量对象的初始化阶段。而let、const在变量对象的初始化阶段是被创建但未初始化的。 |
@ZhangDaZongWei 你好,在 |
这句话我觉得有一丝歧义,const 其实就是不可修改值,比如一个对象
obj 指向的是变量在内存中的地址,这个地址(也就是const声明的标识符的值)不可以被修改,但是这个地址指向的内存空间,也就是name所在的空间所存储的值,是可以随意修改的。 |
其实这个就是跟按值传递同一个类型的问题 |
react 的什么时候出呀 |
关于 let 提升的问题,可以看看这篇方老大的总结, 至于结论,没有这篇文章精彩 |
var变量函数提升放到变量环境,let const放在词法环境。想多了解看yygmind/blog#12 |
i='abc' |
求教 for (let i = 0; i < 3; i++) { console.log(i) } 的伪代码应该是什么样子,如果按这个形式 |
@617429782 在for循环里有特殊处理,可以搜一下知乎方应杭的写的一篇文章,名字我记得大概是终于理解了let |
react啥时候出大佬 |
var i=abc |
请问一下,tdz具体是个啥东西,是会专门开辟一段内存去储存let const这些吗 |
块级作用域存在于 “块中(字符 { 和 } 之间的区域)”, 这句怎么理解呀 |
function init () { for (let i = 0, j = init(); i < 100; i++) { // 最后输出的j都是完全一样的,for循环的第一段变量声明应该只会执行一次,但是会单独开辟一个块级作用域 |
解决了之前一些模棱两可的问题,感谢~ |
因为i在第一次执行之后,被改写成了‘abc’,不符合后续循环条件了 |
邮件已收到,祝一切安好~
|
这是来自QQ邮箱的假期自动回复邮件。你好,你的邮件我收到,谢谢。
|
块级作用域的出现
通过 var 声明的变量存在变量提升的特性:
初学者可能会觉得只有 condition 为 true 的时候,才会创建 value,如果 condition 为 false,结果应该是报错,然而因为变量提升的原因,代码相当于:
如果 condition 为 false,结果会是 undefined。
除此之外,在 for 循环中:
即便循环已经结束了,我们依然可以访问 i 的值。
为了加强对变量生命周期的控制,ECMAScript 6 引入了块级作用域。
块级作用域存在于:
let 和 const
块级声明用于声明在指定块的作用域之外无法访问的变量。
let 和 const 都是块级声明的一种。
我们来回顾下 let 和 const 的特点:
1.不会被提升
2.重复声明报错
3.不绑定全局作用域
当在全局作用域中使用 var 声明的时候,会创建一个新的全局变量作为全局对象的属性。
然而 let 和 const 不会:
再来说下 let 和 const 的区别:
const 用于声明常量,其值一旦被设定不能再被修改,否则会报错。
值得一提的是:const 声明不允许修改绑定,但允许修改值。这意味着当用 const 声明对象时:
临时死区
临时死区(Temporal Dead Zone),简写为 TDZ。
let 和 const 声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,会导致报错:
这是因为 JavaScript 引擎在扫描代码发现变量声明时,要么将它们提升到作用域顶部(遇到 var 声明),要么将声明放在 TDZ 中(遇到 let 和 const 声明)。访问 TDZ 中的变量会触发运行时错误。只有执行过变量声明语句后,变量才会从 TDZ 中移出,然后方可访问。
看似很好理解,不保证你不犯错:
两个例子中,结果并不会打印 "global",而是报错
Uncaught ReferenceError: value is not defined
,就是因为 TDZ 的缘故。循环中的块级作用域
一个老生常谈的面试题,解决方案如下:
ES6 的 let 为这个问题提供了新的解决方法:
问题在于,上面讲了 let 不提升,不能重复声明,不能绑定全局作用域等等特性,可是为什么在这里就能正确打印出 i 值呢?
如果是不重复声明,在循环第二次的时候,又用 let 声明了 i,应该报错呀,就算因为某种原因,重复声明不报错,一遍一遍迭代,i 的值最终还是应该是 3 呀,还有人说 for 循环的
设置循环变量的那部分是一个单独的作用域,就比如:
这个例子是对的,如果我们把 let 改成 var 呢?
为什么结果就不一样了呢,如果有单独的作用域,结果应该是相同的呀……
如果要追究这个问题,就要抛弃掉之前所讲的这些特性!这是因为 let 声明在循环内部的行为是标准中专门定义的,不一定就与 let 的不提升特性有关,其实,在早期的 let 实现中就不包含这一行为。
我们查看 ECMAScript 规范第 13.7.4.7 节:
我们会发现,在 for 循环中使用 let 和 var,底层会使用不同的处理方式。
那么当使用 let 的时候底层到底是怎么做的呢?
简单的来说,就是在
for (let i = 0; i < 3; i++)
中,即圆括号之内建立一个隐藏的作用域,这就可以解释为什么:然后每次迭代循环时都创建一个新变量,并以之前迭代中同名变量的值将其初始化。这样对于下面这样一段代码
就相当于:
当执行函数的时候,根据词法作用域就可以找到正确的值,其实你也可以理解为 let 声明模仿了闭包的做法来简化循环过程。
循环中的 let 和 const
不过到这里还没有结束,如果我们把 let 改成 const 呢?
结果会是报错,因为虽然我们每次都创建了一个新的变量,然而我们却在迭代中尝试修改 const 的值,所以最终会报错。
说完了普通的 for 循环,我们还有 for in 循环呢~
那下面的结果是什么呢?
结果是 'c';
那如果把 var 改成 let 或者 const 呢?
使用 let,结果自然会是 'a',const 呢? 报错还是 'a'?
结果是正确打印 'a',这是因为在 for in 循环中,每次迭代不会修改已有的绑定,而是会创建一个新的绑定。
Babel
在 Babel 中是如何编译 let 和 const 的呢?我们来看看编译后的代码:
编译为:
我们可以看到 Babel 直接将 let 编译成了 var,如果是这样的话,那么我们来写个例子:
如果还是直接编译成 var,打印的结果肯定是 undefined,然而 Babel 很聪明,它编译成了:
我们再写个直观的例子:
本质是一样的,就是改变量名,使内外层的变量名称不一样。
那像 const 的修改值时报错,以及重复声明报错怎么实现的呢?
其实就是在编译的时候直接给你报错……
那循环中的 let 声明呢?
Babel 巧妙的编译成了:
最佳实践
在我们开发的时候,可能认为应该默认使用 let 而不是 var ,这种情况下,对于需要写保护的变量要使用 const。然而另一种做法日益普及:默认使用 const,只有当确实需要改变变量的值的时候才使用 let。这是因为大部分的变量的值在初始化后不应再改变,而预料之外的变量之的改变是很多 bug 的源头。
ES6 系列
ES6 系列目录地址:https://github.com/mqyqingfeng/Blog
ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: