一,let

先看代码:

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

我先花点时间来拆分一下这个for循环,我就写第一个循环过程,后面依次类推:

{
var i = 0; if (i<10) { a[i] = function(){ console.log(i) } }; i++;
} // 第一个循环

{ i = 1;
...;
i++;
}
// 第二个循环

。。。

每一次for循环都是一个代码块,上面用大括号区分开来,es5没有块级作用域的概念,所以等同于没有大括号,i就是全局作用域下的变量,if句里面的代码块是子作用域,因此能访问到它的父作用域,就是for循环的设置循环条件的部分,所以i的值是同一个,就是全局环境下的i,因此for循环执行完成后,i就是10,你不管怎么访问,i都是10;

 

上面的代码要想实现我们预期的效果很简单,把var换成let就可以了,里面的原理是:由于es6有了块级作用域的概念,let声明的i只对它本轮(也就是大括号里的)循环有效,也就是说每一次循环都相当于重新声明了i,所以每次循环(代码块内)都互不干涉,因此能够依次输出不同的i,但是问题来了:如果不是同一个i,为什么i还会叠加呢?每次循环的时候它是访问不到上一轮的i的,因为是不同的块级作用域啊(不同的大括号里)。重点来了:因为JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

 这才是考察的重点!

暂时性死区:在let命令声明变量之前,该变量都是不可用的。

typeof一定百分之百安全吗?es5是这样,es6不是了,因为存在暂时性死区。

关于暂时性死区有一个很有趣的例子,挺绕的:

var x = 1;
fucntion f(y=x){};
f();
// 不报错

var x = 1;
function f(x=x){};
f();
// 报错,注意这里的错误不是x已经被定义了,而是x未定义

 为什么第一个不报错,第二个报错呢?x不应该是取的外面全局下的值吗?

其实第二个里面隐藏了一个暂时性死区:

函数声明时,参数部分实际上就是一个块级作用域y=x,实际上等同于let y = x,x在块级作用域里找不到定义,所以到外部全局下去找,找到了,所以不报错,x=x,实际上等同于let x = x,但此时x不会去块级作用域外面找,为什么,因为块级作用域内部有定义let x,所以它没有找到对应的值,因此报错not defined。

 

块级作用域:

首先,有哪些作用域:全局作用域,块级作用域,还有函数作用域(这个最容易忘,面试的坑)

为什么要块级作用域:1,内层变量可能会覆盖外层变量;2,用来计数的循环变量泄露为全局变量,造成内存泄漏,污染环境。(面试的坑)

 

es5规定,函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明。就比如在if句中声明一个function函数,这就会报错,但在es6中不会。

function f(){ console.log(1); }

(function (){
if (false) { function f(){ console.log(2); }
} f(); }());

上面的代码,如果在es5中运行,if条件中的function会直接跑到if外面去,然后执行,最后输出2。但如果在es6中,允许在块级作用域中声明函数,所以函数不会跑到if条件句外面,那么f()应该调用的是外面的函数声明,理论上应该会输出1,但实际上会报错,为什么?有人解释说也是因为块级作用域里不能声明函数,那把f()放在if语句中并不会报错,说明是可以在块级作用域中声明的。

重点来了:es6的浏览器有自己的行为方式:

1,允许在块级作用域内声明函数;

2,函数声明类似于var,会提升到全局作用域下或函数作用域的头部;

3,函数声明会提升到所在的块级作用域的头部;

有了上面三条,刚刚的代码就等同于下面:

function a(){ console.log(1); }

(function (){
   var f = undefined;
   if (false) {
     function a(){ console.log(2); }
   }
   f();
}());

所以f还没有定义是一个function,因此不能执行。

 

二,const

const大部分的注意点都和let差不多。但是要注意下面的👇

重点:const实际上保证的并不是变量的值不能改动,而是变量指向的内存地址不能改动。如果只是基本类型的值,值就保存在变量指向的内存地址中,等同于常量,如果是引用类型的值,实际上就是保存一个指针,但是const只是保证指针固定,而它指向的数据结构是不是可变,就不一定了。因此,可以给const定义的对象添加属性或者方法,或者给数组添加数据,但是不能重写。

 

这里补充一点,es6声明变量的6种方法:var,function,let,const,import,class

 

posted on 2017-11-23 11:13  言先生  阅读(239)  评论(0编辑  收藏  举报