ES6中的let、const

ES6中的let、const

引言

咱们知道JS这门语言有一套规定了它的基本语法的规范:ECMAscript。以前开发人员使用的都是ES5规范,后来,在2015年6月,ES6终于脱离了草案阶段,正式发布。在JS的ES5(ECMAscript5)规范中,变量的声明就是varfunction这两种,而这两种变量声明方式存在着一些不太合乎常理的问题。好比说很典型的一个问题:变量提高。以上两种方式声明变量都会把这个变量提高到当前做用域的最上面(这里不详细说细节),这其实并不合理,由于咱们声明变量的目的就是去使用它,那么咱们天然是想用它的时候去声明,而后使用。还有一种不太好的地方:for循环里面定义的计数变量是会泄露出去的,而咱们写for循环实际上只想让这个计数变量在当前的for循环内部使用。为了解决这一系列的问题,在ES6中,新增了letconst声明方式,固然也还有一些其余的声明方法先不提,咱们今天只说letconstjavascript

let

使用方法

let的使用和var同样。java

let a = 0
复制代码

而后就能够访问这个变量函数

console.log(a) // 0
复制代码

特色

let声明变量和var声明变量有所区别:学习

不存在变量提高

ES5声明变量:ui

console.log(a) // undefined,存在变量提高
var a = 0
复制代码

let的:编码

console.log(a) // a is not defined,不存在变量提高
let a = 0
复制代码

由于var存在变量提高,打印undefined,而let不存在变量提高,直接就会报错。spa

块级做用域

在ES5里面没有块级做用域的概念,咱们多经过当即执行函数来模仿一块块做用域,把这一块的变量保护起来,同时也防止污染到全局。在ES6里面,加入了块级做用域的概念,就是说letconst声明变量的时候也会把它们绑定到当前的代码块中,这个块咱们称之为这个变量的块级做用域,外部不可访问,就相似于ES5里面的函数内声明的变量。指针

ES5:code

if (true) {
  var a = 0
  console.log(a) // 0
}
console.log(a) // 0
复制代码

这里面在代码块if里面声明的变量a做用域是全局,因此均可访问。对象

ES6:

if (true) {
  let a = 0
  console.log(a) // 0
}
console.log(a) // a is not defined
复制代码

在这里面,let声明的变量a只在这个代码块内有效,外部不可访问,因此会报错。这就是块级做用域。这个块级做用域有许多有意思的地方。请看下面的代码:

function fn () {
    var a = 0
    if (true) {
      var a = 1
      console.log(a) // 1
    }
    console.log(a) // 1
  }
复制代码

在ES5里面,这样写的话,就至关于在if代码块里面后定义的变量a覆盖了前面定义的变量a,因此输出的a的值都是1。那要是用ES6的let声明呢?

function fn () {
    let a = 0
    if (true) {
      let a = 1
      console.log(a) // 1
    }
    console.log(a) // 0
  }
复制代码

在这里由于let声明的变量只在当前的块级做用域内有效,因此,在if代码块里面打印a就是1,在外层打印a就是外层块做用域a的值0,也就是说在这里,父块级做用域里面的变量a用本身在当前块级做用域内的值,子块级做用域的变量a用本身当前块做用域内的值,它们是互不影响的。那么能够引伸出一个let命令的很好的应用场景:for循环。

ES5里面的for循环:

var a = []
  for (var i = 0; i < 10; i++) {
    a[i] = function () {
      console.log(i)
    }
  }
  a[2]() // 10
复制代码

这里面会打印出来10,由于定义在for循环内部的计数变量i其实会泄露为全局变量,因此咱们在执行最后一段代码时,访问到的i就是全局的惟一的这个i,那么这个i就会随着循环改变,可是始终是这个惟一的i。这个时候for循环已经结束,i值变为了10,因此咱们访问哪一个都只会获得10,可是这显然不是咱们想要的,因此let来了。

var a = []
  for (let i = 0; i < 10; i++) {
    a[i] = function () {
      console.log(i)
    }
  }
  a[2]() // 2
复制代码

这里面就很特殊,let声明打这个i只在for循环体内部可用,外部不能访问,因此,这里每一次循环的时候都访问的是当前循环的i,就是说,每一次进入新的循环的时候,i都是一个新的变量i,不是以前的变量i,变量i只在本身当前的循环里面有效,只是在JS的引擎里面会记录i值,后面的新的变量i就会从这个值开始继续循环。因此,咱们这里打印的i值就是2,由于它只能访问它做用域里面的那个变量i

for循环里面还有一点要注意的就是:设置计数变量的部分是一个父做用域,循环体内部是一个子做用域。像下面这样:

for (let i = 0; i < 5; i++) {
    let i = '我是子做用域'
    console.log(i)
  }
  // 我是子做用域
  // 我是子做用域
  // 我是子做用域
  // 我是子做用域
  // 我是子做用域
复制代码

这里面打印了五次我是子做用域,也就是说,设置计数变量的部分是一个父做用域,循环体内部是一个子做用域,他们是互不影响的,这个我前面有说过,各自用本身的块做用域里面的变量的值,就是说:计数变量的i就负责控制循环,循环体里面的i就每次都打印一下就能够了,各司其职。外面的块级做用域不会影响到内层的块级做用域,内层的块级做用域也不会影响外面的块级做用域,各自都是一个封闭的小区域。这里面我总会想到CSS里面的BFC区域(若是不了解BFC自行忽略这句话,没什么影响),和那个差很少。关于块级做用域差很少就这些,咱们继续说下面的内容。

暂时性死区(TDZ)

ES6明确规定,若是区块中存在letconst命令,它所声明的变量就会绑定到当前的块级做用域上,不受外面的影响,这我前面也有说过,那么这个块级做用域就会造成封闭做用域,只要在声明以前使用,就会报错,这就是暂时性死区(TDZ)。最好理解的就是看下面的代码:

if (true) {
    a = 2 // a is not defined
    console.log(a) // a is not defined
    let a
    console.log(a) // undefined
    a = 0
    console.log(a) // 0
  }
复制代码

按照咱们以前所说,只要存在let,那么这个变量a就是当前块级做用域内有效,就造成封闭做用域了,因此咱们在声明以前尝试去给它赋值,打印它,都会报错,由于咱们不能够在声明以前使用他们它,因此在这里,在声明前面的部分就是它的暂时性死区

某些隐蔽的暂时性死区:

function test (a = b, b = 0) {
    return [a, b]
  }
  test() // b is not defined
复制代码

报错的缘由:在变量b声明以前尝试去使用它,这属于b的死区,因此报错。

let a = a // a is not defined
复制代码

咱们知道,赋值运算符先算右边的内容,那么这里在声明a以前尝试获取它,一样属于a的死区,报错。

不容许重复声明

let不容许在相同做用域内重复声明同一个变量。注意:是相同做用域内。

let a = 1
  let a = 1 // Uncaught SyntaxError: Identifier 'a' has already been declared
复制代码

函数内部也不能够从新声明参数:

function fn (arg) {
    let arg
  }

  fn() // Uncaught SyntaxError: Identifier 'arg' has already been declared
复制代码

可是这样作是能够的:

function fn (arg) {
    {
      let arg
    }
  }

  fn() 
复制代码

由于,这时声明的变量就绑定到了当前块级做用域上,不影响到外部,因此它和函数参数的那个变量的做用域也不同了,是被容许的。

const

使用方法

const使用方法和let基本一致。再也不赘述。

特色

前面的特性都和let同样,这里我只说区别。

常量

const声明的是一个只读的常量,一旦声明,常量的值或者指向的地址不得改变。

const a = 1
  a = 2 // Uncaught TypeError: Assignment to constant variable.
复制代码

const因为这个特性,因此它声明常量的同时必须马上初始化(赋值),不然报错。

const a // Uncaught SyntaxError: Missing initializer in const declaration
复制代码

不可变性

实际上const声明常量的不可变是分状况的,若是是简单数据类型的,则值不可变,若是是复杂类型的,则指针不可变,实际对象的属性和方法可变。解释一下,简单数据类型直接把值存储在栈内存当中,因此这个值就不可再改变,复杂类型的数据在栈内存中只存储了一个地址(指针),这个地址不可变,可是这个指向堆内存的地址指向的具体的对象是能够改变的。好比这样:

const obj = {}
  obj.name = 'reslicma'
  console.log(obj) // {name: "reslicma"}
复制代码

咱们只须要知道,const声明的常量保存在栈内存的值都不能改变,可是复杂型数据指向堆内存中具体的对象能够改变就能够了,至于这个栈和堆内存具体内容能够看一下我上一篇文章,里面很详细的叙述了这两者和区别和联系(硬核推荐。。。)。并且ES6学习的话,真的很推荐阮老师的《ES6标准入门》这本书。。。

总结

不是对知识点总结,是说一说ES6引入letconst的做用:之前使用var由于存在变量提高,会形成一些没必要要的错误,或者一些意料以外的咱们不想要的错误,因此加入了letconst避免这类错误,同时也是为了但愿开发人员们养成良好的的编码规范和编码风格。

相关文章
相关标签/搜索