ES6标准学习: 1、let 与 const 命令

1、let命令

一、基本用法

首先看下面一段代码:

var a = []
for (var i = 0; i < 10; i++) {
  a[i] =  function () {
    return i
  }
}
a.map(fn => {
  console.log(fn())
})

以上代码的运行结果可能是什么?

0, 1, 2, ... 9  ?

结果是:连续输出了10次 10

简单的解释就是,当使用map遍历a中的每一个fn时,当fn执行时,在作用域链上查找i时,找到的i是最终已经变成了10的i,而不会是我们所预期的0~9。这是由js这门语言本身所决定的,所以不要觉得奇怪。

那么如何解决?

1、使用闭包与立即执行函数

    var a = []
    for (var i = 0; i < 10; i++) {
      a[i] =  (function (num) {
        return function () {
          return num
        }
      })(i)
    }
    a.map(fn => {
      console.log(fn())
    })

在以上的代码中,我们将a[i]赋值为一个立即执行的函数,并将i作为参数传递进去,而这个立即执行函数内部又返回了一个函数,从而构成了一个闭包,由于闭包可以“捕获”外部函数的变量,因此,里面的这个函数也就“捕获”到了外部这个立即执行函数的num参数,当for循环结束后,尽管i最终还是10,但是每一次的立即执行函数都会创建一个新的闭包,每个闭包都拥有各自的作用域,都会“捕获”到外部num参数并保存,所以输出结果为:

2、使用let声明

使用闭包与立即执行函数的方法固然可以,但是对于很多初学者来说,并不是件很容易的事,而且,过度使用闭包也不是一个好的习惯。

好在ES6增加了let命令,使用let命令所声明的变量,只在let所在的代码块内有效。

1     var a = []
2     for (let i = 0; i < 10; i++) {
3       a[i] =  function () {
4         return i
5       }
6     }
7     a.map(fn => {
8       console.log(fn())
9     })

输出结果为:

在上面的代码中,变量i是let声明的,当前的i只在本轮循环中有效,所以每一次循环的i其实是一个新的变量,所以每一次的i并不是相同的

二、不存在变量提升与暂时性死区

1、不存在变量提升

let命令不会像var那样发生变量声明提升的现象。

 console.log(i) // ReferenceError
 let i  = 1

而如果使用var 来替换此处的i,那么输出的结果将为: undefined

console.log(i) // undefined
var i  = 1

2、暂时性死区

只要块级作用域内存在let命令,那么这个let所声明的变量就“绑定”在了这个区域里

es6规定:如果区块中存在let和const命令,那么这个区块对这些命令所声明的变量从一开始就形成了封闭作用域。只要在声明这些变量之前使用这些变量,就会报错。

总之,在一个代码块内部,使用let所声明的变量,在其被声明之前,都是不可用的,称之为暂时性死区

if (true) {
    // 暂时性死区开始
    val = "123" // ReferenceError
    console.log(val) // ReferenceError

    let val  // 暂时性死区结束
    console.log(val) // undefined
  }

3、使用let,不允许重复声明变量

// 报错
  function test () {
    let val
    let val
  }

二、块级作用域

在es5中,只有全局作用域与函数作用域两种,并没有块级作用域的概念。

首先看下面一段代码:

var val = new  Date()
  function test () {
    console.log(val)
    if (false) {
      var val = "hello world"
    }
  }

以上代码的输出结果如下:

之所以会是undefined,也正由于之前所提到的变量声明提升所造成的,实际上,上面的这段代码中的test函数中的内容被js引擎解析为下面的结果:

function test () {
    var val
    console.log(val)
    if (false) {
      val = "hello world"
    }
  }

所以,输出的结果会是undefined

但是,如果我们使用let的话,就不会存在这样的问题。因为let实际上为js新增了块级作用域这个概念

  function test () {
    let n = 5
    if (true) {
      let n = 10
    }
    console.log(n)  // 5
  }

在上面的函数中,有两个代码块,都声明了变量n,但是运行的结果却是5,这说明外层的代码块并不会受内层代码块的影响。

外层作用域无法读取内层作用域的变量,内层作用域可以定义与外层作用域中相同名字的变量。

那么这里就有一个问题值得思考,我们原来所使用的的立即执行函数,使用立即执行函数的目的是什么? 其实就是为了构造一个块级作用域出来,那么既然let可以构造块级作用域,那么let 可不可以在某些地方代替立即执行函数呢? 答案是可以的

 // 立即执行函数写法
  (function () {
    var val = "hello wolrd"
  })()

  // 块级作用域写法
  {
    let val = "hello wolrd"
  }

三、const命令

const用来声明常量,所谓的常量就是,一旦声明与初始化后,其值就不允许被改变

这也意味着const一旦声明常量,就必须立即初始化

  const PI = 3.14
  PI = 1 // TypeError: Assignment to constant variable

  const val  // SyntaxError: Missing initializer in const declaration

const的作用域与let相同:只在声明所在的块级作用域内有效, 并且const所声明的常量也不会被提升,也同样存在暂时性死区,也不允许重复声明

看下面一段代码:

 const obj = {}
  obj.name = ‘obj‘
  console.log(obj) // Object {name: "obj"}

有没有跟你想象的结果不一样?不是说const所声明的变量被初始化后就不能再被改变了吗?

这是因为:对于复合类型的变量来说,变量名并不指向数据,而是指向数据在内存中存放的地址,const去声明此类的变量,const命令只能保证变量名所指向的地址是不改变的,但是不能保证改地址里面的内容是否发生了变化。

所以,在上面的代码中,const只保证了obj所指向的地址不发生变化, 不保证obj所指向的地址里面的内容是不变化的。

以上就是对let与const命令的简短介绍,如果有什么不对的地方,还请读者指正。

参考书籍: 《ES6标准入门--阮一峰》

时间: 2024-10-26 13:42:53

ES6标准学习: 1、let 与 const 命令的相关文章

ES6标准学习: 2、解构赋值

解构赋值 1.基本使用方法 es6允许按照一定的方式,从数组和对象中提取值,从而对变量进行赋值,这种模式被称为解构. 以前,我们为一个变量赋值,通常这么写: 1 var a = 1 2 var b = 2 3 var c = 3 而在es6中,我们可以这么写: 1 var [a, b, c] = [1, 2 , 3] 上面的代码表示,我们可以从数组中提取值,并按照位置的对应关系对变量进行赋值. 嵌套的解构赋值也是允许的: 1 let [a, [[b], c]] = [1, [[2], 3]] 某

ES6标准学习: 3、数值的扩展

数值的扩展 注:以下内容中: 0 表示数字零, o 为英文字母. 一.二进制和八进制数值表示法 es6提供了二进制和八进制的数值表示法,分别用前缀0b(或者0B)和0o(或者0O)表示. 1 0b111110111 === 503 // true 2 0o767 === 503 // true 从es5开始,在严格模式中,八进制的数值就不允许使用0前缀表示,es6明确表示,要使用0o表示 1 // 非严格模式 2 (function () { 3 console.log(0o11 === 011

ES6标准学习: 4、数组的扩展

数组的扩展 一.类数组对象与可遍历对象转换为数组 Array.from()方法用于将类数组对象.可遍历对象转换为数组,其中可遍历对象包括es6新增的set和map结构 所谓的类数组对象,本质特征是必须拥有length属性 1 let arrayLikeObj = { 2 '0': 'a', 3 '1': 'b', 4 '2': 'c', 5 length: 3 6 } 7 8 ES5转换为数组: 9 let arr = [].slice.call(arrayLikeObj) // ['a', '

ES6教程:let和const命令的用法

ES6中新增了let和const命令,分别用于声明变量和常量. 1. 使用let命令取代var命令 在ES6之前的版本中,使用var声明变量,在ES6中新增了let来声明变量.let完全可以取代var,因为二者的语义相同,而且let没有"副作用". 我们先来看一下let命令有哪些特点: 不存在变量提升:变量只有在声明之后才能使用: 不允许重复声明:在相同的作用域下,一个变量名只能使用一次,不能重复声明: 具有块级作用域:在代码块中声明的变量,只对当前代码块和其内部嵌套代码块有效,如果嵌

《ES6标准入门》10~28Page let和const命令 变量的解构赋值

1.let和const命令 (1)let命令 let命令类似于ES3中的var命令,但是所声明的变量只在let所在的代码块内有效. 1 { 2 let a = 10; 3 var b = 1; 4 } 5 6 console.log(a); //报错 7 console.log(b); //1 let相较var来讲有三个特殊性质,分别是不存在变量提升.暂时性死区和不允许重复声明. 1 //不存在变量提升 2 console.log(a); //报错 3 let a = 1; 4 5 //你无法在

ES6标准入门 第二章:块级作用域 以及 let和const命令

一.块级作用域 1.为什么需要块级作用域? ES5中只有全局作用域和函数作用域,带来很多不合理的场景. (1)内层变量可能会覆盖外层变量: var tem = new Date(); function f(){ console.log(tmp); if(false) { var tmp = "hello world"; } } f(); //undefined 变量提升导致了外层的内层的tmp变量覆盖了外层的tmp变量. (2)用来计数的循环变量泄露为全局变量: var s = &qu

1.《ES6标准入门》(阮一峰)--2.let 和 const 命令

1.let命令 基本用法 let只在命令所在的代码块内(花括号内)有效. for循环的计数器,就很合适使用let命令. //var var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10 //let var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i)

ES6之let(理解闭包)和const命令

ES6之let(理解闭包)和const命令 最近做项目的过程中,使用到了ES6,因为之前很少接触,所以使用起来还不够熟悉.因此购买了阮一峰老师的ES6标准入门,在此感谢阮一峰老师的著作. 我们知道,ECMAScript 6即ES6是ECMAScript的第五个版本,因为在2015年6月正式发布,所以又成为ECMAScript2015.ES6的主要目的是为了是JS用于编写复杂的大型应用程序,成为企业级的开发语言. 说明:由于有时候我们希望得知es6代码的具体实现原理或者说希望能够转化为es5使用,

【学习笔记】ES6标准入门

这里简要记录一下对自己感触比较深的几个知识点,将核心的应用投放于实际的项目之中,提供代码的可维护性. 一.let和const { // let声明的变量只在let命令所在的代码块内有效 let a = 1; var b = 2; } console.log(a); // 报错: ReferenceError: a is not defined console.log(b); // for循环的技术器就很适合let命令 for (let i = 0; i < 3; i++) { console.l