众所周知,js中的var声明存在变量提升机制,因此ESMAScript 6引用了块级作用域来强化对变量生命周期的控制
let const 声明不会被提升,有几个需要注意的点
1、不能被重复声明

假设作用域中已经存在某个标识符(无论该标识符是通过var声明还是let、const变量声明),此时再使用let或const关键定声明会抛错
var count=10
let count=20// 此处则会抛出错误,因为同一作用域内不能重复声明

如果当前作用域内嵌另一个作用域,便可在内嵌的作用域中用let声明同名变量

var count=10
if(true){
let count=20
}

2、const声明的常量必须进行初始化
像下面这样声明就会报错

const name;//语法错误,常量未初始化

3、不可以为const定义的常量再赋值,真正的本质是const声明不允许修改绑定,但允许修改值(也就是说const声明对象后,可以修改该对象的属性值)

const person={
name:'angela'
}
//可以修改对象属性的值
person.name='yun'
// 修改绑定则会抛出语法错误
person={
'name':'Shining'
}

4、临时性死区(Temporal Dead Zone)
JavaScript引擎在扫描代码发现变量声明时,要么将它们提升至作用域顶部(遇到var声明),要么将声明放到TDZ中(遇到let和const声明),访问TDZ中的变量会触发运行时错误,只有执行过变量声明语句后,变量才会从TDZ中移出,才能正常访问
如下代码就因为在if块级作用域内执行console.log时value已经在TDZ中了,以前typeof是一个相对不易出错的操作符,但其实也无法阻挡引擎抛出错误
在声明前访问块级绑定会导致错误,因为绑定在临时死区中

if (true) {
console.log(typeof value)//引用错误
let value = 'blue'
}

而在let声明的作用域外对该变量使用typeof则不会报错

console.log(typeof value)
if (true) {
let value = 'blue'
}

5、块级作用域绑定
之前在循环中创建函数都有些不可言状

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

因为循环内部创建的函数全部都保留了对相同变量的引用,循环结束时变量i的值为10,所以结果会输出10次10
于是大家会在循环中使用立即调用函数表达式,以强制生成计数器变量的副本,以便输出1、2、3......

var funcs = []
for (var i = 0; i < 10; i++) { funcs.push((function (value) {
return function () {
console.log(value)
}
})(i)) }
funcs.forEach(function (func) {
func()
})

有了let,立即调用函数表达式则可以简化,其实每次迭代循环都会创建一个新变量,并以之前迭代中同名变量的值将其初始化

var funcs = []
for (let i = 0; i < 10; i++) {
//其实是每次循环的时候let声明都会创建一个新变量i并将其初始化为i的当前值,所以在循环内部创建的每个函数都能得到属于它们自己的i的副本
funcs.push(function () {
console.log(i)
})
}
funcs.forEach(function (func) {
func()//这里输出是0 然后是1、2....9
})

这个特性同样适用于for in中,举例来说

var funcs = [],
obj = {
a: true,
b: true,
c: true
}
for (let key in obj) {
funcs.push(function () {
console.log(key)
})
}
funcs.forEach(function (func) {
func()//输出的是a b c
})

6、循环中的let声明特性同样适用于const声明,唯一的区别是const不能更改绑定
上面的例子中把let换成const同样输出a b c

var funcs = [],
obj = {
a: true,
b: true,
c: true
}
//之所以可以运用for in 和for of循环中,是因为每次迭代不会修改已有绑定,而是会创建一个新绑定
for (const key in obj) {
funcs.push(function () {
console.log(key)// 同样输出a b c 唯一的区别是循环内不能更改key的值
})
}
funcs.forEach(function (func) {
func()
})

下面的这个例子则会报错,因为在for循环中更改了i的绑定而const常量不能更改绑定

var funcs = []
for (const i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i)
})
}
funcs.forEach(function (func) {
func()
})

7、全局作用域绑定
当var被作用于全局作用域时,它会创建一个新的全局变量作用全局对象(浏览器环境中的window对象)的属性,这意味着用var很可能会无意中覆盖一个已经存在的全局变量

从上图代码中看出即便是全局对象RegExp Array都会被覆盖
但是let或const会在全局作用域下创建一个新的绑定,但该绑定不会添加为全局对象的属性,换句话说用let或const不能覆盖全局变量,而只能遮蔽它

这个时候的RegExp和window.RegExp是不相同的

let RegExp='hello'
console.log(RegExp) //hello
console.log(window.RegExp===RegExp)//false const ncz='hi'
console.log(ncz)
console.log("ncz" in window)

最佳实践:
默认使用let而不是var
默认使用const,只有确实需要改变变量的值时使用let

深入理解ES6之《块级作用域绑定》的更多相关文章

  1. 《深入理解ES6》笔记——扩展对象的功能性(4)

    变量功能被加强了.函数功能被加强了,那么作为JavaScript中最普遍的对象,不加强对得起观众吗? 对象类别 在ES6中,对象分为下面几种叫法.(不需要知道概念) 1.普通对象 2.特异对象 3.标 ...

  2. 深入理解 ES6中的 Reflect

    阅读目录 一:Reflect.get(target, name, receiver) 二:Reflect.set(target,name,value,receiver) 三:Reflect.apply ...

  3. ES6的promise对象研究

    ES6的promise对象研究 什么叫promise? Promise对象可以理解为一次执行的异步操作,使用promise对象之后可以使用一种链式调用的方式来组织代码:让代码更加的直观. 那我们为什么 ...

  4. ES6正则表达式扩展

    前面的话 正则表达式是javascript操作字符串的一个重要组成部分,但在以往的版本中并未有太多改变.然而,在ES6中,随着字符串操作的变更, ES6也对正则表达式进行了一些更新.本文将详细介绍ES ...

  5. ES6函数扩展

    前面的话 函数是所有编程语言的重要组成部分,在ES6出现前,JS的函数语法一直没有太大的变化,从而遗留了很多问题和的做法,导致实现一些基本的功能经常要编写很多代码.ES6大力度地更新了函数特性,在ES ...

  6. ES6数组扩展

    前面的话 数组是一种基础的JS对象,随着时间推进,JS中的其他部分一直在演进,而直到ES5标准才为数组对象引入一些新方法来简化使用.ES6标准继续改进数组,添加了很多新功能.本文将详细介绍ES6数组扩 ...

  7. ES6数字扩展

    前面的话 本文将详细介绍ES6数字扩展 指数运算符 ES2016引入的唯一一个JS语法变化是求幂运算符,它是一种将指数应用于基数的数学运算.JS已有的Math.pow()方法可以执行求幂运算,但它也是 ...

  8. ES6,扩展运算符的用途

    ES6的扩展运算符可以说是非常使用的,在给多参数函数传参,替代Apply,合并数组,和解构配合进行赋值方面提供了很好的便利性. 扩展运算符就是三个点“...”,就是将实现了Iterator 接口的对象 ...

  9. 理解 ES6 Generator-next()方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 前端知识体系:JavaScript基础-原型和原型链-理解 es6 中class构造以及继承的底层实现原理

    理解 es6 中class构造以及继承的底层实现原理 原文链接:https://blog.csdn.net/qq_34149805/article/details/86105123 1.ES6 cla ...

随机推荐

  1. python3中map()函数用法

    python源码解释如下:map(func, *iterables) --> map objectMake an iterator that computes the function usin ...

  2. adb常用命令大全——查看手机设备信息

    查看手机型号 adb shell getprop ro.product.model 查看电池状况 adb shell dumpsys battery 其中 scale 代表最大电量,level 代表当 ...

  3. appium ios 相对坐标点击和控件滑动操作

    环境: 系统:ios 10.13.6 (17G12034) appium:1.14.0 xcode:10.1 iphone:iphone7 12.4 在尝试使用driver.tap([(a,b)],5 ...

  4. JZ-063-数据流中的中位数

    数据流中的中位数 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两 ...

  5. JZ-057-二叉树的下一个结点

    二叉树的下一个结点 题目描述 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 题目链接: 二叉树的下一个结点 代 ...

  6. laravel7 h-ui点改

    html: <td> @if($item->fang_status == 0) <span onclick="changeFangStatus(this,{{$ite ...

  7. MySQL启动报错Failed to open log (file 'D:\phpStudy\PHPTutorial\MySQL\data\mysql_bin.000045', errno 2)

    MySQL报错 191105 9:39:07 [Note] Plugin 'FEDERATED' is disabled. 191105 9:39:07 InnoDB: The InnoDB memo ...

  8. BadImageFormatException异常

    访问页面时,抛出BadImageFormatException异常: 1.如果您的应用程序使用了 32 位组件,请确保该应用程序始终采用 32 位应用程序的运行方式. 如果应用程序项目的"平 ...

  9. 《手把手教你》系列基础篇(八十)-java+ selenium自动化测试-框架设计基础-TestNG依赖测试-番外篇(详解教程)

    1.简介 经过前边几篇知识点的介绍,今天宏哥就在实际测试中应用一下前边所学的依赖测试.这一篇主要介绍在TestNG中一个类中有多个测试方法的时候,多个测试方法的执行顺序或者依赖关系的问题.如果不用de ...

  10. Fedora 阿里云源

    镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 简介 Fedora 是一个 Linux 发行版,是一款由全球社区爱好者构建的面向日常应用的快速.稳定.强大的操作系统.它允许任何人自由地使用.修 ...