众所周知,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. SVN库文件上传操作步骤

    SVN库文件上传步骤: 1,sv st  或 svn status  查看本地库与远程库的差别(M代表文件有修改,?代表目录中有文件本地新增加) 2,根据1步骤中的查看结果:svn diff  差异文 ...

  2. 面试官:Redis中字符串的内部实现方式是什么?

    在面试间里等候时,感觉这可真暖和呀,我那冰冷的出租屋还得盖两层被子才能睡着.正要把外套脱下来,我突然听到了门外的脚步声,随即门被打开,穿着干净满脸清秀的青年走了进来,一股男士香水的淡香扑面而来. 面试 ...

  3. MySQL性能优化之索引设计

    作者:IT王小二 博客:https://itwxe.com 上一篇给小伙伴们讲了关于SQL查询性能优化的相关技巧,一个好的查询SQL离不开合理的索引设计.这篇小二就来唠一唠怎么合理的设计一个索引来优化 ...

  4. MAT工具分析Dump文件(大对象定位)

    前段时间线上服务经常发生卡顿,经过排查发现是大对象引起的Fullgc问题,特此记录排查逻辑. 目录 目的 一.获得服务进程 二.生成dump文件 三.下载mat工具 四.使用mat工具导入第二步生成的 ...

  5. Vue运用swoole 实现多人聊天

    1:项目目录新建php文件和html文件,这里我建了一个swoole文件夹,chat.php 和chat.html 2:将项目同步上线 0.0.0.0 代表的是所有人都可以连接. chat.php代码 ...

  6. think php 公共目录common.php json封装

    <?php function getJsonData($code,$massage,$data){ $result=[ 'code'=>$code, 'massage'=>$mass ...

  7. hive从入门到放弃(二)——DDL数据定义

    前一篇文章,介绍了什么是 hive,以及 hive 的架构.数据类型,没看的可以点击阅读:hive从入门到放弃(一)--初识hive 今天讲一下 hive 的 DDL 数据定义 创建数据库 CREAT ...

  8. 2022最新IntellJ IDEA的mall开发部署文档

    目录 版本说明 一.概述 二.基本构建 三.Git 导入编译器 四.模块描述浅析 五.配置文档 application.yml修改,涉及模块 application-dev.yml修改,涉及模块 ge ...

  9. 合并两个以单链表形式表示的关于x的多项式(基于c语言)

    只写函数内部的,不懂得可以看前面一篇文章对链表的实现: pLinklist addBothLinklist(Linklist* first,Linklist* second){ Linklist *n ...

  10. Java基础——方法重写

    什么是方法重写? 子类中出现和父类中完全一样的方法声明 什么时候可以进行方法重写? 在子类需要父类的功能的同时,功能主体子类有自己的特有内容时,可以重写,一面沿袭了父类的功能一面又定义了子类特有的内容 ...