ES6 对let声明的少数想想

说到ES6的let变量声明,我估摸很多丁会回忆下面几乎个基本点的特色:

  • 没变量声明提升
  • 富有块级作用域
  • 临时死区
  • 非可知重复声明

众多学科以及小结基本还说交了立几碰(说实话大部分篇还差不多,摘录的众),习惯性我还是失去看了MDN上的文档,立马发现一个题材:

In ECMAScript 2015, let will hoist the variable to the top of the
block. However, referencing the variable in the block before the
variable declaration results in a ReferenceError. The variable is in a
“temporal dead zone” from the start of the block until the declaration
is processed.

ECMAScript
2015(即ES6),let会提升变量到代码片顶部。然而,在变量声明前引述变量会造成ReferenceError错误。在代码块开始到变量声明里变量处于暂时死区(temporal
dead zone)。
不得了,看来let是发变量声明提升的好家伙,这个意识引起了本人之兴味。我这去寻觅了一部分系的素材查看,在翻看的长河被,我耶日渐了解了另有饱含的易误解的知识点,下面罗列有连锁材料,方便为生相同兴趣了解的童鞋去查看:

  • 至于对let变量声明的:
    ES6 In Depth: let and
    const
    Are variables declared with let or const not hoisted in
    ES6?
  • js变量作用域概念,比较基础
    What is the scope of variables in
    JavaScript?
  • 即同一篇稿子为充分基础,从作用域,上下文,this,执行上下文,闭包,立即实施函数,等等都摆了相同普,稍微提到了词法作用域(lexical
    scope)即静态作用域。
    Understanding Scope in
    JavaScript
  • 此地透过例子解释词法作用域(lexical scope),很轻掌握
    What is lexical
    scope?
  • 对于for循环中let的见说明
    Why is let slower than var in a for loop in
    nodejs?
  • 其一多事物还提到了,也波及let的作用域到for循环,不过文章好增长,我不过拘留了有关到有。
    You Don’t Know JS: Scope &
    Closures

非甘于失去看资料之就看我下的村办总结吧。

变量声明提升

有关变量声明提升,有几乎独重要:

  • 具备的变量声明( var, let, const, function, function*,
    class)都设有变量声明提升,我们这里只谈谈let变量
  • let被提升至了块级作用域的顶部,表现(或者说换种说法)就是每个let定义的变量都绑定到了手上之块级作用域内。通俗地开口,因为块级作用域在顶部就吧每个let定义之变量留好了位置,所以只要以let变量声明前引述了这变量名,块级作用域都见面发现并弃来荒谬
  • var的变量声明提升会将变量初始化为undefined,let没有初始化,所以发生临时死区的概念。其实打展现上来讲,说let是不曾变量声明提升也生一定道理,因为变量没有在顶部初始化,所以也非可知说变量已经宣称了了,反而用绑定到了手上之块级作用域内这种说法又相信

于自家之思路大致明明白白写就首总结的时刻,我还要奇迹在一如既往篇叙变量声明提升的博文齐视同一段落MDN原文的援:

In ECMAScript 6, let does not hoist the variable to the top of the
block. If you reference a variable in a block before the let
declaration for that variable is encountered, this results in a
ReferenceError, because the variable is in a “temporal dead zone” from
the start of the block until the declaration is processed.

纳尼!居然跟本人本收看的MDN文档不平等……博文的日子是2015-06-11,看来这概念也在转,与时俱进啊。既然如此,我觉着为未尝必要深究了,因为不管概念怎么变,只要会解let在块级作用域的不利表现就是足以了,理论或如呢执行服务。

let在for循环中的表现

for的运行机制

说及for循环,先证下for的运行机制,比如说for(var
i=0;i<10;i++){…}即先初始化循环变量(var
i=0),这等同句子只运行一糟糕,然后进行较(i<10),然后运行函数体{…},函数体运行了后,如果没break等跳出,再运行于增表达式(i++),然后进行较判断(i<10)是否进入执行体。下面是引用他人的一个报How
are for loops executed in
javascript?,将以此历程描述得够呛清晰:

// for(initialise; condition; finishediteration) { iteration }
var initialise = function () { console.log("initialising"); i=0; }
var condition = function () { console.log("conditioning"); return i<5; }
var finishediteration = function () { console.log("finished an iteration"); i++; }
var doingiteration = function () { console.log("doing iteration when `i` is equal", i); }
for (initialise(); condition(); finishediteration()) {
    doingiteration();
}

initialising
conditioning
doing iteration when `i` is equal 0
finished an iteration
conditioning
doing iteration when `i` is equal 1
finished an iteration
conditioning
doing iteration when `i` is equal 2
finished an iteration
conditioning
doing iteration when `i` is equal 3
finished an iteration
conditioning
doing iteration when `i` is equal 4
finished an iteration
conditioning

for循环中的let

所以要独立讲for循环中的let,是以看了阮先生ES6合乎门中言let的那么无异章节的一个例证:

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

针对这个事例原文中是这么的诠释的:

地方代码中,变量i是let声明的,当前之i只以本轮循环中,所以各一样次等巡回的i其实都是一个初的变量,所以最后输出的是6。你或许会见咨询,如果各一样车轮循环的变量i都是更声明的,那她怎么掌握上同一轮循环的价值,从而计算产生本轮循环的价?这是为
JavaScript
引擎内部会铭记上平等轮循环的价值,初始化本轮的变量i时,就以齐亦然车轮循环的底子及进行测算。

JavaScript 引擎内部会记住上一轮循环的值当即词解释自己认为当序猿估计怎么还无法确认吧?记住是词说得极其模糊了,其中虽有某种机制还是规范。而且每一轮循环的变量i都是重新声明,那么下的例子就是不便分解:

for (let i = 0; i < 5; i++){
    i++;
    console.log(i)
}
// 1
// 3
// 5

使循环函数体内之i每次都是又声明的,那么函数体内即子作用域内改变i的价值,为什么能够转移外层定义之i变量?
双重来拘禁文中提的另外一个例证:

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

斯事例原文的诠释是:

循环语句子部分是一个父作用域,而循环体内部是一个独的子作用域。

假设仍地方的逻辑每个子作用域内之i犹再声明,那么在跟一个子作用域内怎么能二糟糕声明?
很明显,i并没再声明。看来我们有必要借助其他文档来赞助了解。

  1. MDN上之文档,提到for循环中,每进来同一差花括号便够呛成了一个块级域,即每个循环进入函数体的i都绑定到了不同之丘级域中,由于是例外的块级作用域,所以每次上循环体函数的i值都互相独立,不是与一个意域下的价。

  2. ES6 In Depth: let and
    const文章中是这般讲的:

    each closure will capture a different copy of the loop variable,
    rather than all closures capturing the same loop variable.

    诸一个闭包(即循环体函数)会捕获循环变量的异副本,而未是都捕获和一个循环变量。这里说明了循环体函数中之循环变量不是简单的援,而是一个副本。

  3. You Don’t Know JS: Scope &
    Closures
    中之解:

    Not only does let in the for-loop header bind the i to the
    for-loop body, but in fact, it re-binds it to each iteration of
    the loop, making sure to re-assign it the value from the end of
    the previous loop iteration.

    let
    不仅在头将i价值绑定到for循环体中,事实上,let将i双重绑定到每个迭代函数中,并包以达到一致浅迭代结束的结果又赋值给i

此间提到的子作用域(for循环的函数体{…}),其实准确地说为词法作用域(lexical
scope),也被称作静态作用域。简单地讲即是以嵌套的函数组中,内部函数可以拜父作用域的变量和其余资源。

构成方面的几乎接触力所能及,子作用域内用的尚是外围声明的i变量,let i = 'abc';不畏相当给以子作用域中宣称新的变量覆盖了父作用域的变量声明。但是子作用域内援的是父作用域变量不是直引用,而是父作用域变量的一个副本,子作用域修改是副本时,相当给修改父作用域变量,而父作用域循环变量改变时,不见面影响子作用域内之副本变量,加多少的即刻词解释说实话还是不曾会说服自己自己,所以我还要找到了stackoverflow上之一个回答。

Why is let slower than var in a for loop in
nodejs?尽管如此未是正面回答for循环的题材,但是里面举的一个Babel实现let的例证也会由var的角度来分解这个题材:

"use strict";
(function () {
  var _loop = function _loop(_j) {
    _j++; // here's the change inside the new scope
    setTimeout(function () {
      console.log("j: " + _j + " seconds");
    }, _j * 1000);
    j = _j; // here's the change being propagated back to maintain continuity
  };
  for (var j = 0; j < 5; j++) {
    _loop(j);
  }
})();

有心人看是事例,外层定义之j变量由形参_j(这里的形参传值,就是动态作用域)传入了循环体函数_loop()中,进入函数体中晚,_j虽相当给他的副本,子作用域可以改父作用域变量(表现于
j =
_j),但_loop()函数执行了晚,父作用域变量j的改无法改变_loop()函数中之形参_j,因为形参_j只会在_loop()函数执行那无异糟糕受赋值,后面外层j价值的改动及外没有关联。回想一下端的问题,如果中还定义了j价,那么尽管会见蒙外层传上的_j(虽然于这事例里j_j变量名无等同,但是在let声明里实际是暨一个变量称),相当给子作用域定义了协调之中以的变量,j = _j;如此这般的赋值语句也绝非意思了,因为这一定给变量自己让协调赋值。

地方就段话是由var实现let的角度来说明,有硌拗口。下面说说自之解,谈谈let变量是怎处理这个历程的:
for循环每次上函数体{…}中,都是上了新的子作用域中,每个子作用域相互独立,新的子作用域引用(实际是变量复制)父作用域的循环值变量,同时可改变量的值且更新父作用域变量,实际见就是和真引用了父作用域变量一样。反之,父作用域无法访问此复制变量,所以父作用域中变量的转移不会见对作用域中的变量有什么影响。但是倘若子作用域中重复声明了是变量名,新的变量就绑定到了子作用域中,变成了子作用域的里变量,覆盖了父作用域的循环值变量,子作用域对新声明的变量的修改都以子作用域范围外,父作用域同样无法访问此变量。

小结

清楚这些概念有时候觉得挺凌乱,好像有些牛角尖,但是我认为只有掌握正确的知道方向,才能够基于实际情形去想、读懂代码,也造福团结写起规范化、易理解的代码。这篇稿子的内容依然是自清楚思路的一个记录,有接触啰嗦,主要为以后好概念模糊后会找到本思想的笔触,由于其中起过多友好的知情,错漏在所难免,也可望大家读后会于自身提出意见跟建议。

正文自:JuFoFu

本文地址:http://www.cnblogs.com/JuFoFu/p/6726359.html

水平有限,错误欢迎指正。原创博文,转载请注明出处。

参考文档:

阮一峰 .
let和const命令

Jason Orendorff . ES6 In Depth: let and
const

You-Dont-Know-JS . You Don’t Know JS: Scope &
Closures

Hammad Ahmed . Understanding
Scope in
JavaScript

MDN
let

MDN
for…of

What is the scope of variables in
JavaScript?

What is lexical
scope?

Why is let slower than var in a for loop in
nodejs?

Are variables declared with let or const not hoisted in
ES6?