一、let

  1.定义

    ES6新增了let命令,用来声明变量,用法类似于var,但是和var有一定的区别

  2.let只在块级作用域内有效

    首先来看一个比较简单的例子,请告诉我,他们分别输出什么

//代码段1
for(var i = 0; i < 10 ;i++){
console.log('我是var声明的')
}
console.log(i)//10
//代码段2
for(let i = 0; i < 10 ;i++){
console.log(‘我是let声明的’)
}
console.log(i)// i is not defined

    两段代码的不同之处就在声明变量i时,一个采用的var,一个采用的let。代码段1在外部打印i时的结果是  10,而代码段2的结果却是  i is not defined。

    这是因为let声明的变量只在块级作用域内有效,所以在外部环境无法使用这个变量,而var声明的变量是一个全局变量。

    然后再看下面这个经典面试题,告诉我,他们分别输出什么

//代码段1 var声明
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i)//10次10
},100)
} //代码段2 let声明
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i)//0-9
},100)
}

    分析代码之前,你要知道setTimeout是一个异步函数,异步函数的执行顺序是当主线程执行完成之后才会执行异步队列里的函数。所以每一次for循环实际上是将与之对应的setTimeout放入异步队列里面。当for循环执行完成之后,再执行这些setTimeout。

    搞清楚了执行过程,那我们从结果来分析原因。用var声明的for循环的结果是10次10,这是因为当for循环执行完成之时,全局变量i的值已经变成了10,调用setTimeout函数,打印10次10。用let声明的变量并不是一个全局变量,是一个有块级作用域的变量,那么在每次循环的时候,这个i就绑定到了对应的setTimeout函数身上,实际上就是每一次循环的i都是一个新变量。所以他会输出0-9。

    园友:你说上面每一次循环的i都是一个新变量,那为什么i的值会依次增加呢?

    小炉:因为 javascript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

  3.不存在变量提升

    var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。我也在之前的博客里面多次分析变量提升的代码执行顺序,在这里不再赘述,后续会专门出一篇变量提升和函数提升的解析博客

    let命令则不会出现这个现象,它要求所声明的变量一定要在声明后使用,否则报错。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2; // let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

    上面的代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

  4.暂时性死区

    只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;

if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}

  上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

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

  总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 tdz)。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

  5.不允许重复声明

    let不允许在相同作用域内,重复声明同一个变量。

// 报错
function func() {
let a = 10;
var a = 1;
} // 报错
function func() {
let a = 10;
let a = 1;
}

    因此,不能在函数内部重新声明参数。

function func(arg) {
let arg;
}
func() // 报错 function func(arg) {
{
let arg;
}
}
func() // 不报错

    值得一提的是,一定要明确不允许重复声明的概念,请看下面的代码:

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

    实际上这段代码并不是重复声明(let不允许在相同作用域内,重复声明同一个变量),for循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

二、const

  1.定义

    const声明一个只读的常量。一旦声明,常量的值就不能改变。

  2.const特点

    (1)const一旦声明变量,就必须立即初始化,不能留到以后赋值。只声明不赋值,就会报错。

    (2)const的作用域与let命令相同:只在声明所在的块级作用域内有效。

    (3)const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

    (4)const声明的常量,也与let一样不可重复声明。

    (5)const声明的变量不得改变值,改变常量的值就会报错

  3.const声明真的不能改值吗?

    const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。

    但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

    接下来我们分别用基本数据类型和引用数据类型举例

//const声明基本数据类型
const i = 10
function show(){
i = 20
}
show()//Uncaught TypeError: Assignment to constant variable. //const声明 数组 改变数组内某个元素
const arr = [1,3,5,67,8,90]
arr[0] = 5
arr.push(4)
console.log(arr)//[5, 3, 5, 67, 8, 90, 4] //const声明 对象 改变对象某个键值
const obj = {
name:'杰西卡',
age:15,
time:true
}
obj[name] = '詹姆斯'
obj.aa = 'hold on'
console.log(obj)//{name: "杰西卡", age: 15, time: true, "": "詹姆斯", aa: "hold on"}

    从上面代码的结果可以看到,const声明基本数据类型的时候,该变量的值是不可以改变的。当声明引用数据类型的时候,对数组/对象内部的元素进行删改,是可以改变的。那这是为什么呢?我们再看一段代码,再来解释

//const声明的对象 重新赋值
const obj = {
name:'杰西卡',
age:15,
time:true
}
let a = {
name:'haha',
age:12,
time:false
}
obj = a
console.log(obj)//Uncaught TypeError: Assignment to constant variable. //const声明的数组 重新赋值
const arr = [1,3,5,67,8,90]
let cc = [0]
arr = cc
console.log(arr)Uncaught TypeError: Assignment to constant variable.

    上面两段代码的结果都是报错,且提示const声明的是无法修改的。

    原因:首先你应该知道引用数据类型的存储方式,在栈中存储了指针,该指针指向堆中具体的对象(也就是对象的值)。const所说的声明变量不能改变值,这个值对于引用数据类型来说是栈中的指针,const只能保证这个指针是固定的。所以当你对对象内部的值进行修改时是被允许的。但是当你去修改这个指针时,就会报错。

三、总结

  let、const声明的变量只能在块级作用域中使用,没有变量提升,不能重复声明,必须先声明再使用。const声明变量必须伴随初始化,且const声明的变量值不可以修改。

  var声明的变量作用域是整个封闭函数,是全局的(浏览器环境下会挂到window上,作为window的属性,let、const不会),var声明的变量存在变量提升,声明会提升到作用域的最顶部。var可以重复声明。

  

  

    

    

    

ES6语法——let和const的更多相关文章

  1. 把JavaScript代码改成ES6语法不完全指南

    目录 * 核心例子 * 修改成静态变量(const)或块级变量(let) * 开始修改 * 疑问解释(重复定义会发生什么) * 疑问解释(let的块级作用域是怎样的) * 疑问解释(const定义的变 ...

  2. ES6语法的学习与实践

    ES6是JavaScript语言的新一代标准,是ECMAScript的第六个版本,加入了很多新的功能和语法,在很多框架,如在使用Vue,React等框架的项目中一般都采用ES6语法来编写的,下面对经常 ...

  3. 如何让浏览器支持ES6语法,步骤详细到小学生都能看懂!

    为什么ES6会有兼容性问题? 由于广大用户使用的浏览器版本在发布的时候也许早于ES6的定稿和发布,而到了今天,我们在编程中如果使用了ES6的新特性,浏览器若没有更新版本,或者新版本中没有对ES6的特性 ...

  4. vue-i18n使用ES6语法以及空格换行问题

    1.运行报错 报错使用了不恰当的exports Uncaught TypeError : Cannot assign to read only property 'exports ' of objec ...

  5. ES6语法知识

    let/const(常用) let,const用于声明变量,用来替代老语法的var关键字,与var不同的是,let/const会创建一个块级作用域(通俗讲就是一个花括号内是一个新的作用域) 这里外部的 ...

  6. 在Node中使用ES6语法

    Node本身已经支持部分ES6语法,但是import export,以及async await(Node 8 已经支持)等一些语法,我们还是无法使用.为了能使用这些新特性,我们就需要使用babel把E ...

  7. es6语法快速上手(转载)

    一.相关背景介绍 我们现在大多数人用的语法javascript 其实版本是ecmscript5,也是就es5.这个版本己经很多年了,且完美被各大浏览器所支持.所以很多学js的朋友可以一直分不清楚es5 ...

  8. webpack中使用babel处理es6语法

    index.js const arr = [ new Promise(()=>{}), new Promise(()=>{}) ]; arr.map(item => { consol ...

  9. Webpack4 学习笔记三 ES6+语法降级为ES5

    前言 此内容是个人学习笔记,以便日后翻阅.非教程,如有错误还请指出 Webpack 将es6.es7语法降级为es5 需要通过 babel JavaScript编译器. 安装: npm i babel ...

随机推荐

  1. 一文搞定Redis五大数据类型及应用场景

    本文学习知识点 redis五大数据类型数据类型:string.hash.list.set.sorted_set 五大类型各自的应用场景 @TOC 1. string类型 1-1 string类型数据的 ...

  2. 用python玩推理游戏还能掌握基础知识点,有趣又充实,你不试试吗?

    可能更多的人依然还在苦苦的学python各种知识点,但其实同样很多人,玩着游戏就把python学会了.     用python玩推理游戏,是这份python教程中的12个游戏的其中之一. 有关这份Py ...

  3. selenium(6)-截取完整页面和指定元素并保存为图片

    截图操作 截取整个页面 截取指定元素 只有这2个方法 比较简单,见下图代码 from selenium import webdriver driver = webdriver.Chrome(" ...

  4. Andrew Ng - 深度学习工程师 - Part 1. 神经网络和深度学习(Week 1. 深度学习概论)

     =================第1周 循环序列模型=============== ===1.1 欢迎来到深度学习工程师微专业=== 我希望可以培养成千上万的人使用人工智能,去解决真实世界的实际问 ...

  5. RedHat服务器安装

    为什么选择 RedHat 市场占有率商业化比较高 厂商的支持比较丰富 新手建议ubuntu 进行上手 等熟悉了Linux环境可以选择自己喜欢的发行版 (有些Geeker就是认为Ubuntu太易于使用了 ...

  6. 并发编程-CPU执行volatile原理探讨-可见性与原子性的深入理解

    volatile的定义 Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量.Jav ...

  7. 网络虚拟化之linux虚拟网络基础

    1 linux虚拟网络基础 1.1 Device 在linux里面devic(设备)与传统网络概念里的物理设备(如交换机.路由器)不同,Linux所说的设备,其背后指的是一个类似于数据结构.内核模块或 ...

  8. ssh -i 密钥文件无法登陆问题

    一.用ssh 带密钥文件登录时候,发生以下报错 [root@99cloud1 ~]# ssh -i hz-keypair-demo.pem centos@172.16.17.104The authen ...

  9. CListBOX 用法

    ListBox的操作比较简单: 1添加数据 声明控件变量的类别为Control,变量类型为CListBox,变量名为m_ListBox_Content. m_ListBox_Content.AddSt ...

  10. Kali Day1

    一.最新版本的Kali Linux的账号名和密码都是kali. 如何切换root  步骤如下: 1. 设置密码 sudo passwd root 2. 切换身份 su 3. 图示 二.快捷键 1. K ...