ES6 let 和 const

在ES6以前,变量的定义通常是使用var关键字,在函数作用域或者全局作用域中使用var定义的变量,都会被提升到顶部(变量声明),这就叫变量声明提升,例子:

function getValue(){

  if(condition){

    var value = 'blue';

    return value;

  }else{

    // 此处可以访问value

    return null;

  }

  // 此处可以访问value

}

上述value变量看似是在condition内声明初始化的,实际上,它的声明会被提升到函数顶部,类似下面:

function getValue(){

  var value;  // 因为没有初始化,值为undefined

  if(condition){

    value = 'blue';

    return value;

  }else{

    // 此处可以访问value

    return null;

  }

  // 此处可以访问value

}

 

1. 块级声明

ES6 中引入了两个关键字let和const来定义块级变量,用于声明在作用域外无法访问的变量,可存在于:

(1)函数内部

(2)块中(字符{和}之间的区域)

let 声明

let声明用法和var一样,用let 来代替 var,可以把变量的作用域限制在当前代码块中

但是let 声明不会被提升,因此通常将let声明语句放在块的顶部,例如:

function getValue(){

  if(condition){

    let value = 'blue';  // 用let声明的变量,当执行流离开了if块,value立刻被销毁

    return value;

  }else{

    // 此处value不存在

    return null;

  }

  // 此处value不存在

}

(1)禁止重复声明

在同一作用域中不能用let重复定义已经存在的标识符,

如果在不同作用域中,则可以

没有报错,在{ }作用域中,let定义中的a 会覆盖全局中的a

 

const 声明

使用const 声明的是常量,一旦定义就无法修改,也会产生块级作用域,但是定义的时候必须同时初始化

同样也只存在于块级作用域,外部无法访问

无法重复声明

const 与 let 的不同之处

 无论是在严格还是非严格模式下,使用const定义的变量都无法再赋值,而let 可以

const 声明对象

 

person 变量指向带有name属性的对象,使用const定义,可以修改其属性的值,但是不能修改其指向

 

临时死区(TDZ)

与var不同,使用const和let声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,即使是相对安全的typeof操作也会触发引用错误,

但是单独执行这个操作

没有报错!!!

为什么呢,因为在输出语句执行的时候,let 初始化ccc的行为还没有执行,

此时的ccc 还位于JavaScript社区所谓的“临时性死区”中,换成const也一样

JavaScript引擎在扫描代码发现变量声明时,要么将它们提升到作用域顶部(遇到var声明),要么将它们放入临时性死区(遇到let,const声明)

访问临时性死区中的变量会触发运行时错误,直到他们被声明(const还需要初始化),变量才会从临时性死区(TDZ)中移出,才可正常访问。

但是在let声明的作用域外访问就不会出错,

 

循环中的块作用域绑定

类似下述代码:

for 循环内用var声明i变量,在for循环外仍然可以访问,因为 i 变量进行了提升

改用let声明变量,则不能在外部访问,

使用var声明,看下列代码

var funcs = [];

for(var i=0;i<10;i++){
  funcs.push(function(){
  console.log(i)
});
}

funcs.forEach(function(func){
  func();
});

我们预期上述输出0-9,但实际呢,输出10次10!

因为循环里每次迭代都共享着同一个i,循环内部创建了10个函数,引用着同一个i,循环结束的时候i的值为10,所以每次调用输出10,

为了解决这个问题,ES5中 我们可以用闭包+IIFE(立即调用表达式)来解决,代码如下:

注意,立即执行表达式外部得用()包起来,IIFE为每一个i变量都创建了一个副本并存储为value

上述代码看似有些多了,在ES6中,我们可以用let来解决这个问题,代码如下:

 

 使用let声明i时,每个循环都会创建一个新的i变量,在一个独立的作用域中,所以内部创建的每个函数都能得到一个自己的i副本

对于for-in 循环 和 for-of 循环也是一样的,示例:

 

 因为每次循环都会创建一个新的key绑定,因此每个函数都有一个key的副本

若使用var声明key,则都会输出c

 

 在循环中若要使用const变量,需要保证后续不会对const所定义的值进行修改,

在普通for循环中会报错,但是在for-in,for-of中可以使用

因为在循环内不会改变key的值

 

全局块作用域绑定

let  和 const 与var 的另一个区别是它们在全局作用域内的行为。

当var被作用于全局作用域时,它会创建一个新的全局变量作为全局对象(浏览器环境中的window对象)

这意味着用var很可能会覆盖一个已经存在的全局变量,就像这样:

但如果你在全局作用域中使用let或const,会在全局作用域下创建一个新的绑定,但该绑定不会添加为全局对象的属性,不会覆盖全局变量,而只能遮蔽它

如下:

所以如果不想为全局创建属性,则使用let和const要安全得多。

 

总结:

当我们使用块级绑定时,默认使用const,只在确实需要修改变量的值时候使用let。这样就可以在某种程度上实现代码的不可变,从而防止某些错误的产生。

posted @ 2018-10-14 22:37  你今天学习了吗  阅读(189)  评论(0)    收藏  举报